pax_global_header00006660000000000000000000000064117267026040014517gustar00rootroot0000000000000052 comment=f8ce375aa5b0d318012e149f5a0b112305fac221 zorp-3.9.5/000077500000000000000000000000001172670260400125275ustar00rootroot00000000000000zorp-3.9.5/.gitignore000066400000000000000000000005031172670260400145150ustar00rootroot00000000000000Makefile.in aclocal.m4 autom4te.cache/ config.guess config.sub configure debian/Makefile.in depcomp install-sh ltmain.sh missing INSTALL Makefile.in Makefile config.status config.log *.o *.lo .deps/ *.la .libs/ libtool stamp-h1 tests/python/runtest.sh zorp-config zorp/zorp zorpblgen/zorpblgen zorpconfig.h zorpctl/zorpctl zorp-3.9.5/AUTHORS000066400000000000000000000020261172670260400135770ustar00rootroot00000000000000 Zorp has been in active development since 2000 and it is result of the hard work of the following people. Zorp Professional Development: Balázs Scheidler Attila Szalay Krisztián Kovács Gábor Simon Design: Márton Illés Péter Höltzl Csaba Major Testing: Tamás Mónos Dezső Moldvai Contributors: Zoltán Györkő András Kis-Szabó Péter Kovács Viktor Tamás Lukács Berki Péter Bolla András Illés Zorp Management System Development: Attila Szalay Andrea Fazekas Dániel Kisházi Edina Pajor Szilárd Holczer Bálint Abonyi Design: Balázs Scheidler Márton Illés Péter Höltzl Csaba Major Testing: Tamás Mónos Dezső Moldvai Zorp Authentication System Development: Balázs Scheidler Attila Szalay Bálint Abonyi Tamás Pálfalvi Testing: Tamás Mónos Dezső Moldvai Zorp Operating System Development: Attila Szalay Balázs Scheidler Sándor Gellér Tamás Pál Krisztián Kovács Testing: Tamás Mónos Dezső Moldvai zorp-3.9.5/BUGS000066400000000000000000000003121172670260400132060ustar00rootroot00000000000000 Reporting bugs -------------- If you need assistance you can find the Zorp user's mailing list at: http://lists.balabit.hu/mailman/listinfo/zorp To submit bugreports, send mail to devel@balabit.hu zorp-3.9.5/COPYING000066400000000000000000000435021172670260400135660ustar00rootroot00000000000000You can find a copy of the GNU General Public license below. This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL as published by the OpenSSL project (http://www.openssl.org) is allowed. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. zorp-3.9.5/INSTALL000066400000000000000000000064351172670260400135700ustar00rootroot00000000000000 ZORP installation ----------------- Zorp requires some preinstalled components to compile properly. Note that ZORP requires the linux kernel version 2.2.x (latest is 2.2.19 as of this writing) with transparent proxy support compiled in. Support for 2.4.x kernels is not complete, you'll not be able to use connection tracking. Required packages: glib 2.0.x python 2.1 or python 1.5 python-extclass 1.2 openssl 0.9.6f Recommended packages: libcap 1.10 for on-the-fly capability management Installation steps: ------------------- 1) Choose an installation prefix (/usr/local/zorp), or simply /usr/local if you plan to use the libraries installed on your system. 2) Install glib 2.0 This step is optional if your system ships glib 2.0. Note that you will need library headers as well which is usually included in the development package. tar xvfz glib-2.0.6.tar.gz cd glib-2.0.6 ./configure make make install 3) Install Python-2.1 or Python-1.5.2 You can either use the package as installed on your system, or compile on your own. Make sure you have development files. Debian distributions create a shared library version of the Python interpreter, Zorp will use this by default. If your distribution doesn't have libpython2.1.so you will have to add a --with-python-libs configure option to Zorp. 4) Install Zorp tar xvfz zorp-xxx.tar.gz cd zorp-xxx ./configure make make install The configure script has a couple of important switches you might need to change. For example: --enable-tproxy[=platform] Turn on transparent proxying support for the given platform. By default an autodetection of your platform is performed which will recognize either linux22/netfilter/ipfilter. The following platforms are available: linux22 -- Linux 2.2 transparent proxy support netfilter -- Linux 2.4 with our Transparent Proxy support patches ipfilter -- assumed on non-Linux platforms (your OS might not be supported, as ipfilter needs to be patched as well) Currently only Solaris is fully supported, otherwise you will not be able to forge the source address of firewall initiated connections. (webserver in the DMZ) --enable-conntrack You can either enable or disable UDP proxy support. CONNTRACK might not be supported on the platform chosen by --enable-tproxy in which cases you may have to disable it. --with-python-headers & --with-python-libs If your system doesn't supply a libpython.so, or places it to a location unknown to this configure script, you'll have to give explicit paths. For example on RedHat you might need something like this: --with-python-headers=/usr/include/python2.1 \ --with-python-libs=/usr/lib/python2.1/config 5) Create your policy Create your policy file in $prefix/etc/zorp/policy.py and $prefix/etc/zorp/instances.conf according to the available documentation. As a beginner reading Zorp Getting Started Guide might be a good starting point. Reference like information is available in python docstrings under /usr/share/zorp/pylib/Zorp/*.py Configure options: ------------------ There are certain features that can be enabled/disabled at compile time. To enable a certain feature add an --enable- on the configure command line. zorp-3.9.5/Makefile.am000066400000000000000000000007421172670260400145660ustar00rootroot00000000000000SUBDIRS = lib libproxy modules zorp pylib doc zorpctl scripts tests DIST_SUBDIRS = $(SUBDIRS) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libzorp.pc libzorpproxy.pc pkgdatadir=@datadir@/zorp pkgdata_DATA = moduledist.conf EXTRA_DIST = dist.conf BUGS module.list WHATIS.TXT VERSION ${pkgconfig_DATA} ${pkgdata_DATA} COPYING: COPYING.in@PACKAGE_SUFFIX@ cp $< $@ MAINTAINERCLEANFILES=dist.conf COPYING dist.conf: dist.conf.in $(top_builddir)/config.status --file $@:$< zorp-3.9.5/README000066400000000000000000000033601172670260400134110ustar00rootroot00000000000000 This is Zorp, welcome. ---------------------- Zorp is a new generation proxy firewall suite making it possible to finetune proxy decisions (with its built in script language), to fully analyze complex protocols (like SSH with several forwarded TCP connections), and to utilize outband authentication techniques (unlike common practices where proxy authentication had to be hacked into the protocol). Quickstarting Zorp ------------------ After installing zorp (described in the file INSTALL), you have to come up with a policy.py file, which may not be the easiest thing to do at first time. A sample policy file is provided named /etc/zorp/policy.py.sample to be renamed to /etc/zorp/policy.py after local modifications are applied. You will need to modify the zone declaration (the beginning of the file), to fit your network architecture. A zone in Zorp is a IP address range, and is the basis of access control. Each zone may define the services which is allowed to go into, and out of the zone. As your policy file is in place, you'll need to add a new entry to your $prefix/etc/zorp/instances.conf file, like this: # excerpt from /etc/zorp/instances.conf zorp_plug --policy /usr/local/etc/zorp/policy-plug.py The first word is the instance name to start, and the rest are the parameters to add to the zorp command line. If you are done, you can now try to start your first Zorp instance using zorpctl: zorpctl start zorp_plug if you leave the instance name empty, all instances are started. Documentation ------------- You can find the Zorp Tutorial in doc/zorp-tutorial.html which describes the GPLd version of Zorp, and a more comprehensive documentation for the commercial version which is available at http://www.balabit.com/support/documentation/ zorp-3.9.5/VERSION000066400000000000000000000000061172670260400135730ustar00rootroot000000000000003.9.5 zorp-3.9.5/WHATIS.TXT000066400000000000000000000121441172670260400141310ustar00rootroot00000000000000What is Zorp? Zorp as you probably know by now is a proxy firewall suite featuring several native application level gateways for different protocols. This document serves as an introduction for you to gasp the ideas behind Zorp. 1. What is a packet filter? --------------------------- Packet filters just as their name suggest filters network traffic by packets. For each individual packet a decision is made whether it is allowed or rejected. Packet filters make their decisions based on packet headers, without checking packet payload. Since common services (like HTTP or FTP) have an assigned port number, access of different services can be controlled by enabling some ports and disallowing others. The problem with this approach is that it is possible to run a service on a port number different from its assigned number. For example you could run a POP3 server on port 80, which is usually assigned to HTTP services. Another problem is that the transmitted information flow is processed in packets. There were several IP stack vulnerabilities (mainly DoS attacks) which were exploitable with incorrectly formatted IP frames. Simple packet filters do not protect against these attacks. 2. What is NAT (Network Address Translation)? --------------------------------------------- NAT is a technique commonly used in packet filters to change the source and/or the destination addresses of forwarded packets. A common scenario is when you have a protected subnet behind a firewall using one of the reserved IP address ranges (192.168.0.0/16) and NAT-ing this address range to the outside world as if it was a single IP address (this is commonly called masquerading). The problem is that once a connection is established from inside, each packet in the reverse direction is automatically forwarded provided that it is sent to the appropriate port on the outside interface. Given that the range of ports which masquerading uses is quite small, one can easily send packets to all ports in that range, some of them will be forwarded to internal hosts. The linux kernel doesn't check by default whether a packet to be demasqueraded was sent from the correct address. 3. What is an application level gateway? ---------------------------------------- Application level gateways (or proxies for short) - unlike simple packet filters - process the information flow at the application level. This means that while packet filters do not touch nor interpret packet payload, an application level gateway implements a specific application protocol (HTTP, FTP or POP3 for instance), and checks whether the dataflow conforms to that given protocol. You cannot send POP3 requests in a HTTP proxy, the proxy will try to interpret POP3 requests as HTTP protocol elements, and since the two protocol differ, the request will fail. Since most security vulnerabilities are exploited with protocol inconforming requests, these - if the application level gateways are well written - can be filtered by these gateways. Application level gateways can bounds-check a given protocol element (check for too long filenames) or look for invalid patterns (filter all filenames containing '../' to prevent dot-dot bug exploits). Since proxies process protocols at this level, they provide higher security in most cases. 4. What is the architecture of a Zorp based firewall? ----------------------------------------------------- Zorp uses both application level gateways and packet filter. However packet filter is only used as an aid, no packet forwarding is done. IPChains filters packets as they enter the system, and denies all unneeded packets. Zorp application proxies are listening on the appropriate interfaces. IPChains is the glue with its REDIRECT target, which intercepts connections and redirects them to the listening proxy. The proxy accepts the connection and acts in the name of the client to initiate further connections to the server. Of course packet filtering is done on the server side too. 5. What is this Python thing in Zorp? ------------------------------------- In addition to providing well written proxies in Zorp, we didn't want to limit you to the settings we wanted to implement. Everything is scriptable, you can define your hooks to modify proxy behaviour. These little scriptlets are written in Python. Additionally Python is used as a glue between different Zorp components, so high level abstractions are implemented in Python. For instance the whole access control can easily be replaced with your own. We are currently using a simple discretionary access control, but we want to implement alternatives, like a TCSEC class B/CC LSPP mandatory access control. The configuration and the firewall policy itself is written in Python too, however we made everything possible to make it look like an ordinary configuration file. 6. What are Zorp instances? --------------------------- Just as any program, you can be launched more than once at the same time. Each instance can have its own configuration and parameters. It is advisable to split your Zorp services to instances. Each Zorp instance is capable of using all protocol proxies, they are stored in and loaded from shared objects. zorp-3.9.5/archived-changelog/000077500000000000000000000000001172670260400162415ustar00rootroot00000000000000zorp-3.9.5/archived-changelog/3.3/000077500000000000000000000000001172670260400165445ustar00rootroot00000000000000zorp-3.9.5/archived-changelog/3.3/ChangeLog000066400000000000000000004015471172670260400203310ustar00rootroot000000000000002008-10-26 Balazs Scheidler * configure.in.in: require version 0 from libwbclient as samba advertises it as such * modules/rdp/rdp_credssp.c: fixed wbclient.h include path * modules/rdp/Makefile.am: use WBCLIENT_LIBS and WBCLIENT_CFLAGS to reference wbclient include/lib paths 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c: partially changed max. line length to 120 (fixes #nobug) * modules/ssh/ssh.c: removed empty line (fixes #nobug) * modules/ssh/sshspecialuserauth.c, * modules/ssh/sshagentforward.c: changed line length to 120, changed format to gnu (fixes #nobug) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshagentforward.c (ssh_agent_resync_global_request): parsing MSG_REQUEST_SUCCESS msg (fixes #16182) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c (ssh_connection_process_channel_open_msg): update channel id within packet (fixes #16181) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c (ssh_uam_kbd_int_process_userauth_info_response_msg): implement functionality of g_hash_table_remove_all of glib-2.12 (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c, modules/ssh/sshuserauth.c, modules/ssh/sshtransport.c, modules/ssh/sshtransport.h, modules/ssh/sshagentforward.c: removed unnecessary agent states, free transport layer's members related to agent forwarding and tl's pkey_auth_blob is renamed to pubkey_auth_blob (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c (ssh_uam_kbd_int_process_userauth_info_response): set to NULL unrefed packets of special userauth (fixes #14961) * modules/ssh/ssh.c (ssh_proxy_free): free non-null variables of special userauth (fixes #14961) * modules/ssh/sshspecialuserauth.h (_SshSpecialUserauthInfo): removed unused member, local_passwd (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c (ssh_uam_kbd_int_process_userauth_info_response): removed code for SSH_SUA_AUTH_PUBKEY state (it happens later now) (fixes #14961) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request, ssh_uam_pubkey_process_userauth_pubkey_ok_msg): added support for special userauth (fixes #14961 and #15526), format changes (fixes #nobug) * modules/ssh/sshuserauth.c (ssh_userauth_process_userauth_request_msg): updated to handle both specialuserauth and agent fwd (fixes #14961 and #15526) * modules/ssh/sshtransport.c: code cleanup: format changes (fixes #nobug) (ssh_tl_process_packet): doesn't check current agent fwd state (fixes #14961 and #15526) * modules/ssh/sshtransport.h (SshTranportLayer): comment changes (fixes #15526) * modules/ssh/sshspecialuserauth.c (ssh_special_userauth_pubkey_request): handling agent forwarding parts (fixes #14961 and #15526) * modules/ssh/sshspecialuserauth.h (SshSpecUserAuthState): added SSH_SUA_STATE_PUBKEY_RESP_SIGNED state (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c, modules/ssh/sshalgo.h, modules/ssh/sshagentforward.c, modules/ssh/sshagentforward.h: cleanups, freeing up memory, validating packets (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c, modules/ssh/sshauthpubkey.c, modules/ssh/sshtransport.c, modules/ssh/sshalgo.h, modules/ssh/sshtransport.h, modules/ssh/sshspecialuserauth.c, modules/ssh/sshagentforward.c, modules/ssh/sshagentforward.h: rewritten resyncing when agent forwarding is enabled. Added ssh_tl_resync_forward(). pubkey_blob to SshKey is happens by ssh_key_from_blob. Lots of cleanup, syntax style changes (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c, modules/ssh/sshconnection.h, modules/ssh/sshagentforward.c, modules/ssh/sshagentforward.h: replaying packets after 'session' request is rewritten, lots of changes (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthpubkey.c, modules/ssh/sshtransport.h, modules/ssh/sshagentforward.c: added body for ssh_agent_resync_pubkey_authentication() ssh_agent_send(), ssh_agent_fetch(): allowing oversized data (splitted to multiple packets) ssh_agent_resync_exchange_public_key(): signing key, too (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshagentforward.c, modules/ssh/sshagentforward.h: comminicating with ssh auth agent at resyncing (fixes #15526) * modules/ssh/sshconnection.c: agent-related functions are moved into sshagentforward.c (fixes #15526) * modules/ssh/Makefile.am: added sshagentforward.[ch] (fixes #15526) * modules/ssh/ssh.c: format changes (fixes #nobug) * modules/ssh/sshtransport.h: ssh_get_message_type() has const parameter (fixes #nobug) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshtransport.h, modules/ssh/sshconnection.c (ssh_connection_process_channel_data_msg, ssh_connection_process_channel_close_msg): new states to keep information where the ssh stays at the current packet. Implemented till exchanging pubkey with ssh auth agent (fixes #1556) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c: rewrites recipient/sender channel ids in forwarded packets (fixes #15526 (#nobug)) * modules/ssh/sshtransport.c: added ssh_tl_prepend_resync_task() (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c, modules/ssh/sshalgo.c, modules/ssh/sshformat.c, modules/ssh/sshauthpubkey.c, modules/ssh/sshuserauth.c, modules/ssh/sshchantcpip.c, modules/ssh/sshchanx11.c, modules/ssh/sshtransport.c, modules/ssh/sshparse.c, modules/ssh/sshkex.c, modules/ssh/sshkex.h, modules/ssh/sshspecialuserauth.c: code cleanup, no more warnings appear with gcc-4.2.3 (fixes #nobug) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.h, modules/ssh/sshconnection.c: ssh_connection_find_channel uses channel's channel_id member instead of channel_ids[side] (fixes #15526) * modules/ssh/sshauthpubkey.c: keep pubkey blob for agent fwd (fixes #15526) * modules/ssh/sshtransport.c: ssh_tl_perform_resync() is now non-static (fixes #15526) * modules/ssh/sshtransport.h: added new states; added pubkey_blob member for transport layer (fixes #15526) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshconnection.c: added resync callbacks, using this when agent request arrives or another packet arrives from the client; (ssh_connection_process_global_request_msg,ssh_connection_process_channel_open_msg, ssh_connection_process_channel_request_msg): checking for agent forwarding (fixes #15526) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request_agent_fwd, ssh_uam_pubkey_process_userauth_pubkey_ok_msg): new state is SSH_AG_ST_SESSION_REQ (fixes #15526) * modules/ssh/sshspecialuserauth.c, modules/ssh/ssh.c: whitespace cleanup (fixes #nobug) * modules/ssh/sshtransport.c (ssh_tl_process_packet): checks for agent_state (fixes #1556) * modules/ssh/sshtransport.h: removed start_time, max_time (fixes #15526) 2008-10-25 Laszlo Attila Toth * ssh_uam_pubkey_request() splitted into 3 similar function, based on the proxy state ( agent fwd and special userauth) * proxy has a new attribute, enable_agent_forwarding * The transport layer keeps actual state of the agent forwarding authentication 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c (ssh_key_verify_x509): Log the server key type and its signature type (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshchansession.c, modules/ssh/sshconnection.c, modules/ssh/sshconnection.h, modules/ssh/sshformat.c, modules/ssh/sshauthpubkey.c, modules/ssh/sshsftp.c, modules/ssh/sshpolicy.c, modules/ssh/sshpolicy.h, modules/ssh/sshuserauth.c, modules/ssh/sshkexdh.c, modules/ssh/sshkexdh.h, modules/ssh/sshsubproto.h, modules/ssh/sshglobals.h, modules/ssh/sshchantcpip.c, modules/ssh/sshchanx11.c, modules/ssh/Ssh.py, modules/ssh/sshtransport.c, modules/ssh/sshnames.c, modules/ssh/sshparse.c, modules/ssh/sshkex.c, modules/ssh/sshformat.h, modules/ssh/sshkex.h, modules/ssh/sshnames.h, modules/ssh/sshtransport.h: whitespace changes, removed trailing whitespaces, unnecessary empty lines (fixes #nobug) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshspecialuserauth.c (ssh_special_userauth_pubkey_validate): added missing z_policy_unlock (fixes #14961) * modules/ssh/Ssh.py: removed debug lines (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c: fixed typo (fixes #14961) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): if pubkey spec. auth fails, sends an SSH_MSG_USERAUTH_FAILURE, whith none as method list (fixes #14961) * modules/ssh/Ssh.py: added specialUserAuthPubkey() with test code (fixes #14961) * modules/ssh/sshspecialuserauth.c (ssh_special_userauth_pubkey_validate): added username parameter for the python callback (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c: set owner->quit to TRUE on error (fixes #14961) * modules/ssh/sshauthpubkey.c: checkinf for special user auth-pubkey auth (fixes #14961) * modules/ssh/sshchanagent.c, modules/ssh/sshauthpassword.c, modules/ssh/sshauthnone.c: whitespace change (fixes #nobug) * modules/ssh/sshuserauth.c (ssh_special_userauth_pubkey_validate): added callback (ssh_special_userauth_parse_request): first 2 item may be the luser and lpasswd (fixes #14961) * modules/ssh/Ssh.py: M * modules/ssh/ssh.c (ssh_main): checks for succesful spec. userauth request parsing (fixes #14961) * modules/ssh/sshspecialuserauth.h, modules/ssh/sshspecialuserauth.c (ssh_special_userauth_pubkey_validate): added callback (ssh_special_userauth_parse_request): first 2 item may be the luser and lpasswd (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c (ssh_uam_kbd_int_process_userauth_info_resonse): if spec.userauth failed it is restarted when it is the first or second attempt. If it succeded, checks for pubkey auth (fixes #14961) * modules/ssh/sshalgo.c (ssh_key_verify_x509): enabled dss1raw for DSA keys (fixes #14012) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): checks for pubkey auth in spec. userauth (fixes #14961) * modules/ssh/sshuserauth.c: auth_on_request is checked when spec. userauth is unused (fixes #1461) * modules/ssh/Ssh.py: added spec.userauth type constants; inband auth enabled. (fixes #14961) * modules/ssh/ssh.c: changes in python interface: special_userauth_request, special_userauth_type local_user are related to spec.userauth and nothing else (ssh_main): checks for self->auth and modify things repectively (fixes #14961) * modules/ssh/sshspecialuserauth.c: (ssh_special_userauth_parse_username): only the first packet's username is parsed, added passwd auth (fixes #14961) (ssh_special_userauth_add_response): request list may empty, local user/passwd are handled specially (fixes #14961) * modules/ssh/sshspecialuserauth.h: more types and states for extended spec. userauth, the 3rd failure drops connection, and pubkey auth added - partially (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c: added EVP_dss1raw(); (ssh_key_sign_x509): signer MD is SHA-1 for RSA, DSS1-RAW for DSA keys (fixes #14012) * modules/ssh/ssh.c: frees SpecialUserauth-related data (fixes #14961) * modules/ssh/sshalgo.h: added EVP_dss1raw() (fixes #14012) * modules/ssh/sshspecialuserauth.c(: replaces old response data (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshspecialuserauth.c: added definition of username parser function (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c (ssh_uam_kbd_int_process_userauth_info_response): Doesn't forward the saved packet (the actual user auth method sends it) (fixes #14961) * modules/ssh/sshalgo.c (ssh_key_verify_x509): added log msg (fixes #nogbug) * modules/ssh/sshuserauth.c (ssh_userauth_process_userauth_request_ms): added kbdint_method_list global variable, saves current method's ID (fixes #14961) * modules/ssh/Ssh.py: specialUserAuth() returns TRUE (fixes #14961) * modules/ssh/ssh.c (ssh_config_set_defaults): sua_info.{request,request_policy} is initialized by NULL value (fixes #14961) * modules/ssh/sshspecialuserauth.c: Several fixes - function names; sets num of prompt for SSH_USERAUTH_INFO_REQUEST, etc. (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshspecialuserauth.c, * modules/ssh/sshspecialuserauth.h, * modules/ssh/sshauthkbdint.c, * modules/ssh/sshuserauth.c * modules/ssh/sshuserauth.h: lots of changes with code (type) cleanups to pass required information to each functions; (ssh_special_userauth_parse_request, ssh_special_userauth_format_packet): added definition (fixes #14961) * modules/ssh/ssh.c (ssh_register_vars): removed specialuserauth_response, added specialuserauth_local_{username,password}_required to python interface (ssh_main): calls ssh_special_userauth_parse_request (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshspecialuserauth.c, * modules/ssh/sshspecialuserauth.h: SpecialUsername-related functions and types are moved to here (fixes #14961) * modules/ssh/sshauthkbdint.c: updated to use reogranized sshproxy (fixes #14961) * modules/ssh/sshpolicy.c: removed ssh_policy_special_userauth_validate() (fixes #14961) * modules/ssh/sshuserauth.c: removed specialusername-related functions (fixes #14961) * modules/ssh/Makefile.am: added sshspecialuserauth.c sshspecialuserauth.h * modules/ssh/ssh.c (ssh_config_set_defaults): hash tables uses g_str_{hash,equal} (ssh_register_vars): name changes (specialusername_*) Whitespace changes (fixes #14961) * modules/ssh/ssh.h: SpecialUsername-related types and members are moved to sshspecialuserauth.h. It is now SshProxy.sua_info (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshauthkbdint.c (ssh_uam_kbd_int_process_userauth_info_response): parse packet for specialusername if necessary (ssh_uam_kbd_int_request): send USERAUTH_INFO_REQUEST packet for specialusername (fixes #14644) * modules/ssh/sshalgo.c (ssh_key_sign_x509): sign with SHA-1 theX509V3_SIGN_RSA keys (ssh_key_set_certificate_blob): free the private key (fixes #14012) * modules/ssh/sshpolicy.c (ssh_policy_special_userauth_validate): added this skeleton (fixes #14961) * modules/ssh/sshuserauth.c: added several function skeleton (ssh_userauth_parse_special_username, ssh_userauth_special_need_kbd_info, ssh_userauth_special_validate_full, ssh_userauth_special_add_response, ssh_userauth_special_format_packet) (ssh_userauth_process_userauth_request_msg): parsing username for specialusername-related settings (fixes #14961) * modules/ssh/sshuserauth.h: added functions for specialusername (fixes #14644) * modules/ssh/Ssh.py: added specialUserAuth() function * modules/ssh/ssh.c, * modules/ssh/ssh.h (SshProxy: added members for SpecialUsername (fixes #14961) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshkexdh.c (ssh_kex_dh_send_kexdh_reply_msg): create the host key (fixes #14012) * modules/ssh/Ssh.py (SshProxy postconfig): updated x509 support due to renamed variables (fixes #14012) * modules/ssh/ssh.c (ssh_config_set_defaults): renamed/new x509 hostkey and cert variables (ssh_get_hostkey): removed hostkey variable checkings (ssh_register_vars): register new variables (ssh_init_key and ssh_init_key): not necessary (ssh_main): removed call of ssh_init_keys (fixes #14012) * modules/ssh/ssh.h (SshProxy): removed hostkeys[] member; added individual private keys for X.509 keys (fixes #14012) * modules/ssh/sshkex.c (ssh_kex_setup_proposal): check whether variables for X.509 host keys are empty or not (fixes #14012) 2008-10-25 Laszlo Attila Toth * modules/ssh/Ssh.py (SshProxy): fixed errors related to new variables, host_key_*_keypair (fixes #14012) * modules/ssh/ssh.c (ssh_proxy_free): freeing up host keys (fixes #14012) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c, modules/ssh/sshalgo.h (ssh_key_set_from_blob): wrapper of ssh_key_set_*_blob (fixes #14012) * modules/ssh/sshkexdh.c (ssh_kex_dh_send_kexdh_reply_msg): do not free up hostkey (fixes #14012) * modules/ssh/Ssh.py (SshProxy): added host_key_rsa_keypair and host_key_dss_keypair tuples (private key, certificate) (fixes #14012) * modules/ssh/ssh.c (ssh_config_set_defaults): set hostkey algos to x509v3-sign*,ssh-*, so the default is the first with X.509 (ssh_get_hostkey): added support of x509v3-sign* and check the result of ssh_key_set_openssh_privfile_cert (ssh_init_key, ssh_init_keys): new functions to load all available host keys for the client side (ssh_main): call of ssh_init_keys and if it fails, return immediately (fixes #14012) * modules/ssh/ssh.h (SshProxy): new members for host keys and certificates (fixes #14012) * modules/ssh/sshkex.c (ssh_kex_check_algos, ssh_kex_select_kex_algo, ssh_kex_select_kex_hostkey_algo): checking algos for kex and hostkey (ssh_kex_choose_algorithms): check the algos for kex and hostkey and if it was unsuccessful, it is an error (fixes #14012) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c: added 'static' keyword for most functions with documentation (ssh_key_{verify,sign}_*) (fixes #14012) * modules/ssh/sshkexdh.c: replaced functions with ssh_key_{verify,sign} (fixes #14012) * modules/ssh/sshauthpubkey.c, modules/ssh/ssh.c: removed unnecessary blank lines (fixes #14012) * modules/ssh/sshalgo.h: removed non-public functions (ssh_key_{verify,sign}_*) (fixes #14012) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c (ssh_key_sign_finish) and (ssh_key_sign_finish): don't free the ctx (fixes #14012) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c: (ssh_cipher_evp_map): became const Added ssh_key_verify{,rsa_dss,x509} functions and use only ssh_key_verify from anywhere (fixes #14012) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): freeing up created packet (fixes #nobug) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): using ssh_key_verify function for every type of keys (fixes #14012) * modules/ssh/ssh.c: whitespace changes (fixes #nobug) * modules/ssh/sshalgo.h: added ssh_key_verify (fixes #14012) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c: added wrapper functions for ssh-dsa, ssh-rsa and x509v3-sign-* signing (ssh_key_set_openssh_privfile_cert): set X.509 certificates (ssh_key_set_certificate_blob): removed unused parameter (ssh_key_set_certificate_blob): added X.509 validation stub (ssh_key_get_pubkey_blob): added x509v3-sign* types (fixes #14010) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): doesn't verify incoming keys and certificates, temporarily. Signing both old pubkeys and certificates (fixes #14010) * modules/ssh/ssh.c (ssh_config_set_defaults): different host key algs (ssh_map_userkey): code cleanup (fixes #14010) * modules/ssh/sshalgo.h: added ssh_key_sign wrapper function (fixes #14010) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c (ssh_key_set_type, ssh_key_free): added X509V3_SIGN* names ssh_key_set_openssh_privfile renamed to ssh_key_set_openssh_privfile_cert and supports X509V3_SIGN* (ssh_key_set_certificate_blob): freeing previously allocated RSA/DSA keys (fixes #14010) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): SSH_NAME_X509V3_SIGN* name changes (fixes #14010) * modules/ssh/ssh.c (ssh_map_userkey): implemented support of X.509 certifcates (fixes #14010) * modules/ssh/sshnames.txt: removed x509v3-sign, x509v3-sign-*-sha1 (fixes #14010) * modules/ssh/sshalgo.h: ssh_key_set_openssh_privfile is a wrapper of ssh_key_set_openssh_privfile_cert (fixes #14010) 2008-10-25 Laszlo Attila Toth * modules/ssh/ssh.c (ssh_map_userkey): Creating PEM format from the X.509 certificate of the userkey (fixes #14010) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshalgo.c: added ssh_key_set_certificate_blob to parse received certificate blob (fixes #14010) * modules/ssh/sshauthpubkey.c (ssh_uam_pubkey_request): added support of X.509 pubkeys (ssh_map_userkey): added stub for x509 certificates(fixes #14010) * modules/ssh/ssh.c (ssh_config_set_defaults): added X.509 host keyalgorithms but commented out (fixes #14010) * modules/ssh/sshnames.txt: changed order of x509v3-sign* (fixes #14010) * modules/ssh/sshalgo.h (_SshKey): added X509 member Added ssh_key_set_certificate_blob (fixes #14010) 2008-10-25 Laszlo Attila Toth * modules/ssh/sshnames.txt: Added x509v3-sign* names (fixes #14012) 2008-10-25 Simon Gabor * modules/rdp/*: enabled crypt types made configurable (fixes: #13235) 2008-10-25 Simon Gabor * modules/rdp/rdp_mangle.c: leftover junk code removed (fixes: #13235) 2008-10-25 Simon Gabor * modules/rdp/rdp.c: mem leak fixed (fixes: #13667) 2008-10-25 Simon Gabor * modules/rdp/rdp_crypt.c, rdp_data.c, rdp_iso.c, rdp_rdp4.c, rdp_licence.c: missing checks on inclusive length fields added (fixes: #13833) 2008-10-25 Simon Gabor * modules/rdp/*: enabled crypt types made configurable (fixes: #13235) 2008-10-25 Simon Gabor * modules/rdp/rdp_mangle.c: leftover junk code removed (fixes: #13235) 2008-10-25 Simon Gabor * modules/rdp/rdp_crypt.c, rdp_data.c, rdp_iso.c, rdp_rdp4.c, rdp_licence.c: missing checks on inclusive length fields added (fixes: #13833) 2008-10-25 Simon Gabor * modules/rdp/rdp_mangle.c: reset requests on unknown devices allowed to pass through (fixes: #15399) 2008-10-25 Simon Gabor * modules/rdp/rdp_iso.[hc], rdp_mangle.c: crypt type 'credssp' added (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp_credssp.[hc]: credssp packet support started (fixes: #12762) * modules/rdp/rdp.[hc]: credssp parsing integrated into the existing processing sequence (fixes: #12762) * modules/rdp/rdp_common.[hc]: ber class and tag numbers joined, misnamed 'ber_result' renamed to 'ber_enum', 'ber_mcs_domain_params' to 'ber_sequence', definition for standard ber tags added (fixes: #12762) * modules/rdp/rdp_initreq.[hc], rdp_initrsp.[hc]: changes required by new ber parsing code done (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp_credssp.[hc]: ntlm_challenge and ntlm_authenticate added (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp.c: (rdp_deinit_stream) typo fixed (fixes: #12762) * modules/rdp/rdp_credssp.[hc]: parsing of ntlm_restrictions and av_pair added, preserving of ntlm_challenge and ntlm_authenticate made conditionally configurable (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp.c: credssp mangling added (fixes: #12762) * modules/rdp/rdp_drawing.c: logging of bitmap data moved to log level 9 (fixes: #12762) * modules/rdp/rdp_iso.[hc]: crcc proto 'credssp_or_ssl' added, double logging of fastpath data commented out (fixes: #12762) * modules/rdp/rdp_mangle.[hc]: starting and stopping of credssp phase fixed (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/*: credssp man-in-the-middle started (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp_credssp.c: mitm faking of cssp pubkeyauth packets fixed (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp.[hc], rdp_credssp.h: mitm state variable added (fixes: #12762) * modules/rdp/rdp_initrsp.[hc]: RC4_NONE support of mcs_init_rsp_crypt fixed (fixes: #12762) * modules/rdp/rdp_mangle.c: cssp mitm client-side implemented (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp.[hc]: two additional proxy domain attributes propagated to policy level, top-level processing loop restructured in order to support substitution of regular iso packets by cssp ones (fixes: #12762) * modules/rdp/rdp_credssp.c: support for partial cssp packets added (fixes: #12762) * moduesl/rdp/rdp_mangle.c: cssp mitm server-side started (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp.[hc]: original server ssl certificate obtained (fixes: #12762) * modules/rdp/rdp_data.c: log format of rdp5 logon info password fixed (fixes: #12762) * modules/rdp/rdp_mangle.c: generating of fake server-side ntlm authenticate message added (fixes: #12762) * modules/rdp/rdp_credssp.[hc]: processing of AuthInfo blocks started, threadsafeness of some constant logging functions fixed (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp.[hc]: storing of server ssl pubkey added, variable nomenclature unified, conditionals moved to the common header (fixes: #12762) * modules/rdp/rdp_data.c: logging of passwords conditionally removed (fixes: #12762) * modules/rdp/rdp_credssp.[hc]: log levels fine-tuned, handling of cssp_credentials and cssp_password_creds added, unneeded MITM levels removed (fixes: #12762) * modules/rdp/rdp_mangle.c: unneeded MITM code removed, reset of unknown rdpdr device is now tolerated, another MS bug at x509 cert algo fixed, server-side cssp MITM completed (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/Rdp.py: attributes 'proxy_netbios_name' and 'proxy_dns_name' propagated to policy level (fixes: #12762) * modules/rdp/rdp.[hc]: obsolete comments, unused variables and unneeded log messages removed, memory leaks eliminated (fixes: #12762) * modules/rdp/rdp_crypt.c: comment added for leak-suspicious assignment (fixes: #12762) * modules/rdp/rdp_mangle.c: processing of RDP4_DATA_SET_ERROR fixed, obsoleted code snippets removed, ms bad x509 algo workaround improved, error handling improved (fixes: #12762) * modules/rdp/rdp_rdp4.[hc]: type rdp4_data_disconnect renamed to rdp4_data_set_error, missing rdp4 capability type constants added (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp_credssp.c: freeing of wbcAuthUserInfo implemented here (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp_credssp.c: reference to the location of the wbclient header fixed (fixes: #12762) 2008-10-25 Pal Tamas * configure.in.in: Added pkg_check for wbclient. * debian/control.in-pro: Added libsmbclient-dev as build-dependency 2008-10-25 Simon Gabor * modules/rdp/rdp_common.[hc]: empty licence cert bug fixed (fixes: #14910) 2008-10-25 Simon Gabor * modules/rdp/rdp_credssp.c: winbind header location fixed (fixes: #12762) 2008-10-25 Simon Gabor * modules/rdp/rdp_mangle.c: empty licence cert bug fixed (fixes: #14910) 2008-10-25 Simon Gabor * modules/rdp/rdp_rdpdr.c: typo fixed in rdpdr devices available rDAD processing code (fixes: #15399) 2008-10-20 Balazs Scheidler * lib/audit.c, lib/ifmonitor.c, lib/pyaudit.c, lib/timestamp.c: fixed some gcc4 warnings * lib/audit.c (z_audit_trail_write_record): don't memset the memory pointed to by the sign_priv_key pointer, it belongs to a Python string object 2008-10-20 Balazs Scheidler * zorp/audit/audit.h (ZAuditSessionParams): changed constructor/destructor functions to indicate that they do not allocate/free the instance itself (e.g. rename them to _init, _deinit) * lib/proxy.c (z_proxy_policy_start_audit_method): adapted to ZAuditSessionParams changed * lib/pyaudit.c (z_policy_audit_parse_global_params): new function, code mostly moved out of z_read_global_params to parse global audit related parameters, (z_policy_audit_parse_session_params): new function, parses startAudit Python argument list into a ZAuditSessionParams structure 2008-10-20 Laszlo Attila Toth * lib/timestamp.c (timestamp_thread): removed unnecessary *free() calls, which were caused sigsegvs (fixes #15444) 2008-10-20 Laszlo Attila Toth * modules/vnc/vnc.c (vnc_policy_get_audit_stream): set self->enable_audit if the audit stream is created (fixes #15364) 2008-10-20 Laszlo Attila Toth * modules/vnc/vnc.c (vnc_main): fixed log msg (fixes #15363) 2008-10-20 Laszlo Attila Toth * modules/vnc/vnc.c (vnc_main): checks for return value of vnc_init_audit(), shuts down proxy if it is FALSE (fixes #15363) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_main_loop): checks for successful audit system startup (fixes #15921) * lib/audit.c, lib/zorp/audit.h: z_audit_init has gboolean return value, TRUE means successful startup (fixes #15921) 2008-10-20 Laszlo Attila Toth * lib/audit.c: removed z_audit_log_ssl_error(); log OpenSSL error messages when an error occured (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/proxy.c (z_proxy_policy_start_audit_method): removed "digest_" prefix from sign_* (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_init): Fixed typo in log msg (fixes #15280) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_digest_record): readded ! to EVP_SignFinal() (fixes #15267) 2008-10-20 Laszlo Attila Toth * lib/audit.c: added z_audit_log_ssl_error(); for RSA keys the digest message algorithm is the SHA-1, for DSA, it is DSS-1 (fixes #15267) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_digest_record): rsa_pkt changed to sign_pkt (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c: replaced nullc/onec by unset/set (fixes #14644) 2008-10-20 Laszlo Attila Toth * configure.in.in: removed check for openssl/ssl.h; fixed typo (fixes #14644) * lib/audit.c (z_audit_trail_write_timestamp), lib/timestamp.c: added notifications (fixes #14644) * lib/audit.c (z_audit_trail_write_header): checks the certificate against the private key (fixes #15260) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_new): close fd only if it is already opened (fixes #15201) 2008-10-20 Laszlo Attila Toth * lib/proxy.c (z_proxy_policy_start_audit_method): code cleanup (fixes #14644 * lib/audit.c (z_audit_session_params_free): doesn't memset the private key (fixes #15201) * lib/audit.c (z_audit_trail_free): writes record only when the audit trail file is opened (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_new): If an error occured, unlinks the current audit trail file and sets fd to -1 (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_header): log if certificate is not set, and set trail's error member on errors (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c, lib/timestamp.c: gurl.h -> zurlparse.h (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (_ZAuditEncInfo): added BIO* array for DER-formatted certificates Added z_audit_trail_get_x509_der() to create X509 and BIO object from a PEM-formatted string of certificate (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c: writes the HMAC key length to the encryption info header; keeps it as a member of ZAuditTrail (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_read_private_key): encrypted private keys are handled as an error, Zorp won't ask the passphrase, (z_audit_trail_new): log msg changes; logs if the trail is signed/timestamped (fixes #14644, #15100, #15095) * modules/vnc/Vnc.py: Removed startAudit and stopAudit Python functions (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp/proxy.h, * lib/proxy.c: passing kw_args to get_audit_stream proxy function (fixes #14644) * modules/telnet/telnet.c, * modules/telnet/telnet.h: implemented get_audit_stream(), removed startAudit() and audit_session for this proxy, in favour of ZProxy's; added "version" info to the z_audit_stream_init(fixes #14644) * lib/audit.c (z_audit_trail_write_digest_record): continue the digest (fixes #14644) * modules/ssh/ssh.c: added kw_args to ssh_policy_get_audit_stream (fixes #14644) * modules/rdp/rdp.c: M modules/rdp/rdp_audit.c, modules/rdp/rdp_audit.h, modules/rdp/rdp_policy.c, modules/rdp/rdp_policy.h: modified code for using ZProxy's startAudit() method (fixes #14644) * modules/vnc/vnc.c: implemented functions for startAudit/startSession python functions (fixes #14644) * modules/vnc/vnc.h: removed cert_list_obj member (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c, pylib/Zorp/Config.py: added config.audit.timestamp_length (fixes #14644) * lib/zorp/proxy.h: added get_audit_stream function to proxy functions (fixes #14644) * lib/proxy.c: Implemented startAudit() function (fixes #14644) * lib/zorp/audit.h, lib/audit.c, modules/ssh/sshconnection.c. modules/ssh/sshconnection.h: ZAuditTrailInitInfo is renamed to ZAuditSessionParams (fixes #14644) * modules/ssh/ssh.c: implemented get_audit_stream() function (fixes #14644) * lib/zorp/pyaudit.h, lib/pyaudit.c: removed z_policy_audit_stream_init (fixes #14644) * lib/timestamp.c, lib/zorp/timestamp.h: functions got z_ prefix; GAtomicCounter is replaced by ZRefCount (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): renamed members of audit_params; audit_params.timestamp_length is changed here (fixes #14644) * lib/proxy.c (z_proxy_free_method), lib/zorp/proxy.h (ZProxy): removed audit_params-related members (fixes #14644) * pylib/Zorp/Zorp.py, pylib/Zorp/Config.py: renamed config.audit.*sign* (fixes #14644) * lib/zorp/audit.h: renamed/removed members of audit_params related to signature; removed *initinfo_{ref,unref}, session's info member (fixes #14644) * lib/audit.c: hmac_keys and private keys are temporarily in the memory, if they are used once, memset'd. The audit trail's RSA/DSA signing key is passed as an EVP_PKEY pointer instead of its low-level version. The *InitInfo is passed from z_audit_stream_init to the corresponding functions as function parameter. It can be null anywhere. (fixes #14644) * lib/zorp/pyaudit.h, lib/pyaudit.c: ZAuditTrailInitInfo is passed via callback parameter instead of the proxy itself (fixes #14644) * modules/ssh/sshconnection.c (ssh_connection_process_channel_open_msg), modules/ssh/ssh.c (ssh_policy_start_audit_method_cb): updated as of the current implementation of the audit system (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c, lib/zorp/audit.h, lib/pyaudit.c: whitespace changes (fixes #nobug) * modules/vnc/vnc.c (vnc_ready): changed for the new z_audit_stream_init() (fixes #14644) 2008-10-20 Simon Gabor * modules/vnc/vnc.[hc]: audit trail cert list made configurable (fixes: #14288) 2008-10-20 Laszlo Attila Toth * lib/audit.c: z_audit_read_digest_sign_private_key's code is moved to z_audit_read_private_key which is also used by the z_audit_trail_new to support RSA/DSA signing keys specified as startAudit parameters (_ZAuditTrail): copied signing-related audit parameters Using audit_parameter.* if it is not optional (e.g compress, but encrypt may differs based on startAudit parameters) (fixes #14644) * lib/zorp.c, lib/zorp/proxy.h, lib/proxy.c, modules/ssh/sshconnection.c, modules/ssh/sshconnection.h, modules/telnet/telnet.c, lib/audit.c, lib/zorp/audit.h, modules/ssh/ssh.c, lib/pyaudit.c, modules/rdp/rdp_audit.c: ZProxy's certs member is replaced by audit_info, which contains certificate lists, key and cert of the digital signature in digest records. It is a reference-counted type, however, ZAuditStream has a borrowed reference (fixes #14644) 2008-10-20 Laszlo Attila Toth * pylib/Zorp/Config.py, lib/zorp/audit.h: audit_params.rsa_sign renamed to digest_sign * lib/audit.c (z_audit_stream_init): 2 new params for digest key and certificate; log the changes in audit_param.* if it is really changed (fixes #14644) 2008-10-20 Laszlo Attila Toth * configure.in.in: removed PKG_CHECK_MODULES, and constants related to libzaudit (fixes #14644) * debian/control.in-pro: removed libzaudit dependency (fixes #14644) * lib/zorp/audit.h: copied definitions from "zorp/audit/libzaudit.h" (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): function name changes (fixes #14644) * lib/zorp/proxy.h: added digest_sign_private_key, digest_sign_certificate (fixes #14644) * lib/proxy.c (z_proxy_free_method): freeing up new members (fixes #14644) * modules/rdp/rdp_policy.h, modules/ssh/ssh.c, modules/telnet/telnet.c, modules/rdp/rdp_policy.c: changed layout of the start_audit_methods and callbacks. Using kw_args. (fixes #14644) * lib/pydict.c (z_policy_method_call): uses all parameters (fixe #14644) * modules/ssh/sshconnection.c (ssh_connection_start_channel_audit): proxy.cert is not set here (fixes #14644) * lib/zorp/pyaudit.h, lib/pyaudit.c (z_policy_audit_stream_init): parsing keywords; sets audit stream as parameter - cleanups (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/proxy.c (z_proxy_free_method): frees up certificate list (fixes #14644) * lib/audit.c, lib/zorp/audit.h: added z_audit_certs_free. * lib/zorp/pyaudit.h, * lib/pyaudit.c: added z_py_audit_stream_init as a wrapper for startAudit function calls. The differences are handled via a callback, z_py_audit_stream_get_stream_cb (fixes #14644) * modules/ssh/ssh.c, modules/rdp/rdp_policy.c, modules/telnet/telnet.c: uses new functions (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c: whitespace changes (fixes #nobug) * lib/timestamp.c: whitespace changes; (timestamp_thread): freeing up TS-related vars (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c: added FIXED_TIMESTAMP_LENGTH define and timestamp_length, currently the gap for the timestamp has a fixed length, 3 kiB, it enables this, otherwise it would come via Config.py, which is not yet implemented (_ZAuditTrailTimestamp): added max_length, to hold actual gap size, which won't be changed on policy reloading... (z_audit_trail_write_timestamp, z_audit_trail_create_timestamp): ... uses it (z_audit_trail_write_header): writes gap size if timestamping is enabled (fixes #14644) * lib/timestamp.c: removed debug code (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): fixed typo (fixes #14644) * lib/timestamp.c, lib/zorp/timestamp.h: starting a separate thread for each timestamp request, currently at most 30 parallel. The code is almost the same as in the syslog-ng-pe-3.0 (fixes #14644) * lib/zorp/Makefile.am, lib/Makefile.am: added timestamp.[ch] (fixes #14644) * lib/audit.c: removed the code which talked with the timestamp request, added a placeholder for the timestamp data, which is initialized with a lots of zero (z_audit_trail_create_timestamp): initializes data for the timestamping thread (z_audit_trail_write_timestamp): writes the timestamp to its final place if it is large enough (z_audit_trail_write_digest_record): writes the timestamp gap to the end of the record (z_audit_trail_flush, z_audit_trail_write_encryption_info): updateing trail->file_ofs (z_audit_init): initializing timestamping (fixes #14644) * lib/zorp/audit.h (_ZAuditParams): added timestamp_length to indicate the length of the largest possible timestamp response which is still supported (fixes #14644) 2008-10-20 Laszlo Attila Toth * configure.in.in: ZORP_LIBS contains $OPENSSL_LIBS; removed unnecessary subst of OPENSSL_CPPFLAGS 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): read config.audit.timestamp_url (fixes #14644) * pylib/Zorp/Config.py: added config.audit.timestamp_url (fixes #14644) * lib/audit.c: added functions for timestamping (fixes #14644) * lib/zorp/audit.h: added timestamp_url to the the audit params (fixes #14644) 2008-10-20 Laszlo Attila Toth * configure.in.in: Cleanup in the OpenSSL checkings; min. version is 0.9.8; checks for the openssl/ts.h (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_header): close file on error (z_audit_trail_encrypt_key_by_pubkey): unused, removed (z_audit_trail_init_encryption_list): initializing all variables, and encrypt the HMAC key by RSA_public_encrypt function; unnecessary g_free-s are removed (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params), pylib/Zorp/Zorp.py, pylib/Zorp/Config.py, lib/zorp/audit.h (_ZAuditParams): the private and public key used for signing the digest record is renamed, they are called now as digest_private_key and digest_certificate, respectively, because both RSA and DSA signing is available (fixes #14644) * lib/audit.c (audit_params, z_audit_trail_write_header, z_audit_read_digest_rsa_private_key): using the new names for digest private key and certificate (z_audit_trail_write_digest_record): signing the digest, not the packet (z_audit_trail_encrypt_key_by_pubkey): removed unused var, dsa (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_header): use ZA_FF_SIGN; write certificate of the signature (z_audit_trail_write_digest_record, z_audit_trail_encrypt_key_by_pubkey, z_audit_read_digest_rsa_private_key): added code DSA signature (z_audit_trail_init_encryption_list): added memory cleanup code (fixes #14644) * lib/zorp/audit.h (_ZAuditParams): added members for DSA signature 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): read parameters for RSA signature and timestamping (fixes #14644) * pylib/Zorp/Zorp.py: read RSA private/public key for the RSA signature of the digest record (fixes #14644) * pylib/Zorp/Config.py: added parameters for RSA signature and timestamping (fixes #14644) * lib/audit.c (_ZAuditTrailHash): hmac_key is guchar; (_ZAuditEncInfo): added variables for hmac computing Removed AUDIT_TRAIL_HEADER_LEN constant (z_audit_trail_write_header): HMAC/SHA1 init is removed; added code for writing out RSA pubkey/certificate Added z_audit_trail_init_signature() to initialize the HMAC/SHA1 digest algos (z_audit_trail_write_digest_record): write record type, file and strim ids, relative timestamps as in normal records; the record length is added to the file length. Extra param for forcing the timestamping/signing (z_audit_trail_write_encryption_info): write mac key as encrypted keys, encrypted by the pubkey (certificate). Enc. info doesn't apper in the digest Added z_audit_trail_encrypt_key_by_pubkey() to encrypt hmac by a certificate (z_audit_trail_init_encryption_list): initialize encrypted hmac keys (z_audit_trail_new): initialize signature/digest (z_audit_read_digest_rsa_private_key): loads the private key and fill digest_rsa audit parameter. It disables rsa_sign param on failure (fixes #14644) * lib/zorp/audit.h (ZAuditParams): added params for RSA-signed digest record (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): reads the filename and loads its content for RSA signing (fixes #14644) * lib/audit.c (z_audit_trail_write_digest_record): writes the RSA signature added z_audit_read_digest_rsa_private_key_file() to init the RSA type for signing the record (fixes #14644) * lib/zorp/audit.h (_ZAuditParams): added digest_rsa, digest_rsa_private_key_file (fixes #14644) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): read config.audit.digest_rsa_private_key_filename (fixes #14644) * pylib/Zorp/Config.py: added config.audit.digest_rsa_private_key_filename (fixes #14644) * lib/audit.c (_ZAuditTrail): packing holes (fixes #nobug) (z_audit_trail_write_digest_record): implemented without timestamp and RSA sign (fixes #14644) * lib/zorp/audit.h (_ZAuditParams): packing holes (fixes #nobug) 2008-10-20 Laszlo Attila Toth * lib/audit.c, lib/zorp/audit.h: whitespace cleanup: trailing spaces are removed, and unnedded extra lines (fixes #nobug) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): Added write_digest_time_diff (fixes #14644) * pylib/Zorp/Config.py: added config.audit.write_digest_time_diff, seconds between two timestamps are written (fixes #14644) * lib/audit.c: added functions and types for the DigitallySignedTrail: struct ZAuditTrailHash, z_audit_trail_digest_record, z_audit_trail_write_digest_record. (z_audit_trail_write_header): initializes the digest algos (z_timeval_subtract): changed implementation (originally: timeval_subtract) The digest contains all plaintext data, trail header, encryption info and the actual diretion's record header and data (fixes #14644) * lib/zorp/audit.h (ZAuditParams): added write_digest_time_diff for timestamping/digitally signed trail (fixes #14644) 2008-10-20 Laszlo Attila Toth * modules/telnet/telnet.c (telnet_collect_meta): remove ';' after the if command (ret becomes FALSE if and only if the condition is true) (fixes #14075) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_init): If compression is enabled, compression level has to be in range 1..9 (fixes #14022) 2008-10-20 Laszlo Attila Toth * lib/pyaudit.c (z_py_audit_read_certificate_list): doesn't increase certs->cert_list_num, if no cert list is available (fixes #13566) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_stream_init): if the certs parameter contains at least one certificate, encryption is enabled (fixes #14007) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_encryption_info, z_audit_trail_new): fixed mistakes caused by refresh (fixes #14010) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_record): removed line that writes record length + 8 into the packet buffer (fixes #13994) 2008-10-20 Laszlo Attila Toth * lib/zorp/audit.h (ZAuditParams): reopen_time_threshold is guint64 (fixes #nobug) 2008-10-20 Laszlo Attila Toth * lib/audit.c (ZAuditTrail): removed gz and cipher members (z_audit_trail_write_record): self->gzs[index] is used (fixes #13972) 2008-10-20 Laszlo Attila Toth * lib/audit.c: ZAuditEncInfo: all_cert_lens and all_cert_num are unsigned; (z_audit_trail_write_record): padding_buf and ol is unsigned char; (z_audit_trail_write_encryption_info): guint j; (z_audit_trail_write_encryption_info) info[i] is not a pointer, using '.' for accessing memembers (fixes #13566) 2008-10-20 Laszlo Attila Toth * pylib/Zorp/Zorp.py (init): fixed typo when loading certificates (fixes #13566) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): added config.audit.directions (fixes #13566) * pylib/Zorp/Zorp.py: If config.audit.encrypt_certificate is set and the ..._list variable is not used, the certificate cames from config.audit.encrypt_certificate and the directions are limited to 1 (fixes #13566) * modules/telnet/telnet.c (telnet_connection_start_channel_audit): added support of certificate lists (fixes #13566) * lib/audit.c: (z_audit_trail_write_record): the audit trail file contains unencrypted data length instead of encrypted due to fixed block sizes. Using explicit cipher padding. (z_audit_trail_write_encryption_info): fixing pointer derefence problem. Changed order of info members (z_audit_trail_init_encryption_list): added memory-freeing code (z_audit_trail_init_encryption): freeing up unused memory (z_audit_trail_free): using internal count of lists (z_audit_stream_init): if required count of lists is missing, it is an error(fixes #13566) * lib/zorp/audit.h: removed direction specific enums, they are moved into libzaudit (fixes #13566) * modules/ssh/ssh.c: M * lib/pyaudit.c (z_py_audit_read_certificate_list): the list count is increased by one even if the list is empty (fixes #13566) * modules/rdp/rdp_audit.c (rdp_channel_start_audit): using certificate lists (fixes #13566) * modules/rdp/rdp_policy.c (rdp_policy_set_audit_method): reading certificate lists from python code (fixes #13566) 2008-10-20 Laszlo Attila Toth * lib/audit.c (z_audit_trail_write_record): added cipher padding (z_audit_trail_write_encryption_info):_writing encription info in a different order (z_audit_trail_init_encryption_list): set header_len correctly (z_audit_trail_init_encryption): check if optional certificate list is a null pointer * lib/zorp/pyaudit.h, lib/pyaudit.c: renaming parameters 2008-10-20 Laszlo Attila Toth * lib/zorp.c: removed audit.h include. (z_read_global_params): read config.audit.encrypt as Integer (fixes #nobug) * lib/audit.c (z_audit_stream_commit_meta): removed asasertatioin. (z_audit_init): checking audit_params.certs.cert_list_lens[0] to enable/disable audit encryption (fixes #13566) * lib/pyaudit.c (z_py_audit_read_certificate_list{,s}): using PyList_GET_ITEM instead of PyList_GetItem (fixes #13566) 2008-10-20 Laszlo Attila Toth * lib/zorp.c (z_read_global_params): moved reading of certificate lists to pyaudit.c (z_py_audit_read_certificate_lists) (fixes #13566) * lib/zorp/Makefile.am: added pyaudit.h (fixes #13566) * lib/zorp/proxy.h (ZProxy): Added member (certs) for optional certificate lists (fixes #13566) * lib/Makefile.am: added pyaudit.c (fixes #13566) * modules/ssh/sshconnection.c, modules/ssh/sshconnection.h (ssh_connection_start_channel_audit): added parameter for cert. lists (Fixes #13566) * lib/audit.c: using ZAuditCertList type instead of multiple parameters (fixes #13566) * lib/zorp/audit.h: new type, ZAuditCertList (fixes #13566) * modules/ssh/ssh.c (ssh_policy_start_audit_method): reading certificate lists (fixes#13566) * lib/pyaudit.h, * lib/pyaudit.c: pyhton interface for reading certificate lists: z_py_audit_read_certificate_lists (fixes #13566) 2008-10-20 Laszlo Attila Toth * lib/zorp.c: (z_read_global_params) Reading encryption list. New functions: z_read_global_certificate_lists, z_read_global_certificate_list (fixes #13566) * pylib/Zorp/Zorp.py: Reading encryption list from files (fixes #13566) * modules/ssh/sshpolicy.c (ssh_dump_meta), modules/ssh/ssh.c (ssh_policy_start_audit_method), modules/telnet/telnet.c (telnet_collect_meta, telnet_policy_start_audit_method), modules/ssh/sshconnection.c (ssh_connection_start_channel_audit): replaceing z_audit_stream_commit_meta() with z_audit_stream_commit_meta_recv_sent() (fixes #13566) * pylib/Zorp/Config.py: new vairables for the certificate lists * lib/audit.c: New format of the audit trails. The session object contains extra certificates coming through startAudit function. Several changes in the parameter lists and extra functions if necessary. (fixes #13566) * lib/zorp/audit.h: Audit stream no longer contains last_record_stamp. Constants for the directions of the trail. New wrapper function, z_audit_stream_commit_meta_recv_sent to commit meta both SEND and RECV directions (fixes #13566) * modules/rdp/rdp_audit.c (rdp_audit_desktop), modules/rdp/rdp_policy.c (rdp_policy_set_audit_method): only RECV direction is used for commit_meta (fixes #13566) 2008-10-18 Balazs Scheidler * lib/pypolicy.h (z_proxy_session_get_policy): removed this function as the prototype is inherently racy * lib/proxy.c (z_proxy_set_authorization_verdict): new function, combined the code of the earlier z_proxy_session_get_policy and z_policy_authorize into a single function to avoid races (fixes; #13850) * zorpctl/main.c: make "authorize -l" produce similar output to gui-status * pylib/Zorp/Zorp.py: moved external_auth_{accept,reject} to this file from Zorp.Auth 2008-10-18 Laszlo Attila Toth * pylib/Zorp/Auth.py (ExternalAuthorization.isAuthorized): removed conversion of session_id to string (fixes #13850) * zorpctl/main.c: added functions for listing pending sessions, necessary data comes as "zorpctl szig -r zorp.authorization.pending" (fixes #13850) * zorpctl/szig.c, * zorpctl/szig.h: removed function z_szig_authorize_list_pending (fixes #13850) * lib/szig.c (z_szig_handle_command): removed handler of AUTHORIZE_LIST command (fixes #13850) 2008-10-18 Laszlo Attila Toth * pylib/Zorp/Session.py (MasterSession.setServiceInstance): introducing master_session_id member, such as svc:/svc:0 (fixes #13850) * pylib/Zorp/Auth.py: typo fixes; (ExternalAuthorizatin.isAuthorized): using session's master_session_id; storing current timestamp as floating point number (fixes #13850) * lib/zorp/proxy.h, lib/proxy.c: added z_proxy_session_get_policy (fixes #13850) * lib/zorp/policy.h, lib/pypolicy.c added z_policy_authorize to wrap Pyhton functions for calling from the C code (fixes #13850) * lib/szig.c (z_szig_handle_command): implementing AUTHORIZE command (z_szig_init): added BEGIN/END mark(fixes #13850) 2008-10-18 Laszlo Attila Toth * pylib/Zorp/Auth.py: new class, ExternalAuthorization; new functions: external_auth_authorize and external_auth_reject to be used from C code (fixes #13850) * pylib/Zorp/Globals.py: added variables for external authorization (fixes #13850) * pylib/Zorp/Proxy.py: fixing typos (fixes #nobug) * zorpctl/main.c (z_cmd_authorize): if no parameters are specified, fall back to listing (fixes #13850) * zorpctl/szig.c (z_szig_authorize_list_pending): using one command for authorize list (fixes #13850) * lib/szig.c: removed previously added z_szig_agr_auth_pending* (z_szig_handle_command): added dummy implementation for AUTHORIZE* commands (z_szig_init): changed handlers for AUTH_PENDING events(fixes #13850) 2008-10-18 Laszlo Attila Toth * pylib/Zorp/Auth.py (AuthorizationPolicy.performAuthorization): raise AAException when self.authorization.isAuthorized fails (fixes: nobug) * pylib/Zorp/Proxy.py (Proxy.userAuthenticated): catch raised exceptions of performAuthorization (fixes #nobug) 2008-10-17 Laszlo Attila Toth * lib/zorp/szig.h: Z_SZIG_AUTH_PENDING_* values indicating waiting for authorization (fixes #13850) * zorpctl/main.c: new function, z_process_authorize_list and z_cmd_authorize for "authorize" (fixes #13850) * lib/szig.c: added event handlers for authorization (fixes #13850) * zorpctl/szig.c, zorpctl/szig.h: new functions for authorize (fixes #13850 2008-10-17 Laszlo Attila Toth * zorpctl/main.c: added z_process_authorize (z_cmd_authorize): added call of z_process_authorize* (fixes #13850) * zorpctl/szig.c: (z_szig_authorize_list_pending): removed unused variable (z_szig_authorize): added description to szig command (fixes #13850) * zorpctl/szig.h: added description parameter to z_szig_authorize (fixes #13850) 2008-10-17 Laszlo Attila Toth * zorpctl/main.c (z_cmd_authorize): if the instance parameter is set, lists pending authorizations only in this proxy instance (fixes #13850) 2008-10-17 Laszlo Attila Toth * pylib/Zorp/Auth.py (AuthorizationPolicy.performAuthorization): raise AAException when self.authorization.isAuthorized fails (fixes #nobug) * pylib/Zorp/Proxy.py (Proxy.userAuthenticated): catch raised exceptions of performAuthorization (fixes #nobug) 2009-03-12 Szalay Attila * VERSION: Bumped to 3.3.2.4 2008-10-22 Czilly Gergely * configure.in.in: don't specify bogus default value for gperf path; produce error if no gperf was found. (fixes: #12242) * modules/pssl2/Pssl.py (X509KeyBridge.getKeypair): Use "DSA-SHA1" hash algorithm if the keypair is for DSA. (fixes: #14380) * tests/functional/ftp/func/cases/bug13633.tests: Added test cases for NLST-ing an empty directory and getting a 150/226 or 226 answer. (fixes: #13633) * modules/ftp/ftp.c (ftp_data_server_connected): Don't consider being unable to build the data connection fatal (that is, don't end the FTP session if it happens). (fixes: #13633) * modules/ftp/ftpcmd.c (ftp_command_answer_path): Clean up any active data connection on a 2xx answer if there was no 150 answer before it. (fixes: #13633) 2008-10-22 Szalay Attila * lib/ifmonitor.c (z_ifmon_parse_ifinfo): Clearing if_name and if_group before looking for them in the message so that no garbage will be returned there. (fixes: #14303) * lib/attach.c, lib/pyattach.c, lib/zorp/attach.h, pylib/Zorp/Chainer.py, pylib/Zorp/Router.py: Implement and document local port randomization. (fixes: 15718) * modules/ftp/ftpcmd.c (ftp_parse_nums) Reject empty parameter because it's not a valid number list. (fixes: #12342) * tests/functional/ftp/func/cases/bug12342.tests: Added testcase. (fixes: #12342) * modules/ftp/ftpcmd.c (ftp_parse_nums, ftp_command_parse_PORT): Added documentation. (fixes: #nobug) 2008-10-21 Simon Gabor * modules/rdp/rdp_audit.[hc]: keyboard information sent as a dedicated record (fixes: #15018) * modules/rdp/rdp.h, rdp_audit.c, rdp_initreq.[hc], rdp_mangle.c: keyboard details added to audit trail (fixes: #15018) * modules/msrpc/msrpc.c: raw packet data logged at debug/8 (fixes: #14301) * modules/msrpc/msrpcparse.c: array freeing bugs fixed, missing z_enter added (fixes: #14301) * modules/msrpc/msrpcforward.c: forwarder race fixed (fixes: #14301) * modules/vnc/*.[hc]: type and function names transcribed according to the coding policy, threadsafeness bugs fixed in 'xxx_name()', 'error' attributes renamed to 'error_str', argument cloning bugs fixed in create_set_pixel_format() and create_set_encoding() (fixes: #14289) 2008-10-20 SZALAY Attila * configure.in.in: Remove unused --enable-conntrack configure option. (fixes: #nobug) 2008-10-20 Balazs Scheidler * modules/anypy/anypy.c (anypy_set_verdict): new function, exported to Python to set a verdict (fixes: #13874), various other changes (fixes: #13874) * lib/pystream.c (z_policy_stream_readline): new function, adds a readline method if the stream is a ZStreamLine, added nul_nonfatal, split attributes to ZPolicyStreams, added GIOStatus to the returned values 2008-10-19 Balazs Scheidler * lib/proxy.c (z_proxy_query_stream): new function, returns client_stream & server_stream attributes, (z_proxy_config_method): added client_stream & server_stream attribute registrations, (z_proxy_destroy_method): destroy py_endpoints * debian/control.in-gpl: added python-dns dependency * debian/zorp.files: added Notification.py 2008-10-19 MOLDVAI Dezso E. * scripts/xmlparts/pfilter.xml: Removed obsolete OUTPUT chain from tproxy table (fixes: #13127) 2008-10-19 Viktor Hercinger * modules/telnet/telnet.c (telnet_stream_write): Record all data in both directions (fixes: #15066) * modules/telnet/telnet.c (telnet_process_buf): Remove data-only audit recording (fixes: #15066) * modules/telnet/telnet.c (telnet_write_audit_record): Added function to record telnet data to audit. (fixes: #15066) 2008-10-19 Simon Gabor * pylib/Zorp/Dispatch.py, Session.py, Zone.py, Config.py: cache threshold options moved to 'config.options.' (fixes: #11855) 2008-10-19 Laszlo Attila Toth * modules/rdp/rdp.c (rdp_main): added z_proxy_loop_iteration to the main loop (fixes #13855) 2008-10-19 Balazs Scheidler * lib/pypolicy.c: fixed gcc4 warnings * lib/szig.c: -"- * lib/plugsession.c: -"- * lib/proxy.c: -"- * lib/tpsocket.c: -"- * lib/audit.c: -"- * lib/pybalance.c: -"- * modules/pssl2/*.c: -"- * lib/zorp/policy.h, lib/pypolicy.c: added signed/unsigned variants of var_parse functions * lib/zorp/audit.h (ZAuditParams): changed some members to signed types to avoid warnings in zorp.c * pycore.c (z_py_szig_event): fixed the type of the PyDict_Next position argument, it used to be an "int" but that PyDict_Next expects a size_t, I wonder why this has not crashed on 64 bit platforms. * fixed some suspicious warnings in several modules 2008-10-14 SZALAY Attila * VERSION: Bumped to 3.3.2b 2008-10-07 Szalay Attila * modules/imap/imapauth.c (imap_auth_login_line): Removed password from string from log messages when the password is too long. (fixes: #15965) * modules/ftp/ftpcmd.c (ftp_command_parse_PASS): Removed password string from log messages when the password is too long. (fixes: #15965) 2008-10-06 Szalay Attila * Forward-ported patches from v3.1 (712-712) 2008-10-03 Szalay Attila * lib/plugsession.c (z_plug_session_destroy): Moved z_plug_session_unref into the if because this function may called when self is NULL. (fixes: #15906) 2008-10-02 Szalay Attila * pylib/Zorp/Chainer.py (ConnectChainer.establishConnection): Fixed a typo. (fixes: #nobug) (SideStackChainer.chainParent): Start the chained proxy and return with the client stream. (fixes: #nobug) * lib/plugsession.c (struct _ZPlugSession): Added reference counting. (fixes: #15906) (z_plug_copy_data): Checked if plug session is still alive. (fixes: #15906) (z_plug_session_init_streams, z_plug_session_init_stacked_streams): Changed stream callbacks to use reference counting. (fixes: #15906) (z_plug_session_ref, z_plug_session_unref): New function to handle reference counting. (fixes: #15906) (z_plug_session_destroy): Renamed z_plug_session_free to follow semantic changes. (fixes: #15906) * modules/msrpc/msrpcforward.c, modules/plug/plug.c, modules/pssl/pssl.c: Changed z_plug_session_free calls. (fixes: #15906) * configure.in.in: Explicitly set -O0 when compiling in debug mode. (fixes: #nobug) * lib/pyproxy.c (z_policy_proxy_init_instance): Log if instance start called with wrong parameters. (fixes: #nobug) 2008-09-18 Szalay Attila * pylib/Zorp/Proxy.py (Proxy.connectServer): Removed codes from here. (Moved to Chainer.py) (fixes: #15560) * pylib/Zorp/Chainer.py (ConnectChainer.establishConnection): Moved szig event linked to successfull server connection here. (fixes: #15560) Moved notify event linked to unsuccessfull server connection here. (fixes: #15560) 2008-09-16 Szalay Attila * VERSION: Bumped to version 3.3.2a 2008-09-12 Szalay Attila * modules/rdp/rdp_mangle.c (rdpdr_mangle): Fixed device reset problem. (fixes: #nobug) 2008-08-08 Szalay Attila * VERSION: Bumped to 3.3.2 2008-07-31 Szalay Attila * modules/vnc/Vnc.py (AbstractVncProxy): Added default values. (fixes: #nobug) * modules/rdp/Rdp.py (AbstractRdpProxy): Added new attribute host_keypair_rsa_file to be able to add rsa key and cert from GUI. (fixes: #nobug) (AbstractRdpProxy): Reverted host_key_cert_file and host_key_rsa_file types to string. (fixes: #nobug) (AbstractRdpProxy.__post_config__): If host_keypair_rsa_file exists set host_key_cert_file and host_key_rsa_file from it. (fixes: #nobug) 2008-07-30 Szalay Attila * modules/rdp/Rdp.py: Fixed cert and key file type. (fixes: #14487) * modules/pssl2/Pssl.py: Removed duplicate documentation. (fixes: #14488) 2008-07-23 Szalay Attila * VERSION: Bumped to 3.3.1.2 2008-07-23 Laszlo Attila Toth * lib/pydispatch.c (z_policy_dispatch_bind_new_instance_iface): reads from /etc/iproute2/rt_ifgroup (fixes #14317) 2008-07-22 SZALAY Attila * VERSION: Bumped to 3.3.1.1 2008-07-20 SZALAY Attila * modules/msrpc/msrpcforward.c, modules/rsh/rsh.c: Added tproxy marking to socket options. (fixes: #14029) 2008-07-20 Fekete Robert * Minor corrections in the proxy documentations * Corrections and updates in the Chainer docs * Corrections and updates in the Dispatcher docs, obsoleted listener and receiver * Corrections and updates in the NAT docs * Corrections and updates in the Sevice docs * Corrections and updates in the Sockaddr docs * Corrections and updates in the Stream docs * Corrections and updates in the Stack docs * Corrections and updates in the zone docs * Corrections and updates in the notification docs * Corrections and updates in the Config.py * Added a HTTP->HTTPS redirection example to the refguide * Corrected a typo in the msrpc doc, fixes bug13826 * created a man page for kzorp * Final updates for the release of ZOrp refguide 3.3.0 * Typo fix for bug 13893 * Removed BalanceNAT fixmes (related to bug 13305) * Fixed XML syntax errors 2008-07-20 Balazs Scheidler * lib/proxy.c (z_proxy_free_method): added log message at core.debug(7) to inform the system log that a given ZProxy instance was freed. This is useful to diagnose memory leaks (fixes: #nobug) 2008-07-16 Simon Gabor * modules/vnc/vnc.[hc], Vnc.py: display size limitation feature removed (fixes: #13712) 2008-07-15 Szalay Attila * Forward-ported patches from 3.1 (688-704) * modules/vnc/vnc.c (vnc_ready): Fixed compilation problem in mainline. (fixes: #nobug) 2008-07-15 Simon Gabor * /modules/vnc/vnc.c: argument list for z_audit_stream_init fixed, missing designators of structure initaliser for 'vnc_proxy_funcs' added (fixes: #14220) * modules/imap/imapparse.c: (imap_parse_number) erroneous calls to 'z_proxy_return' fixed (fixes: nobug) * modules/rdp/rdp.c: missing designators added to structure initialiser for 'rdp_proxy_funcs' (fixes: nobug) * modules/vnc/*: vnc proxy forward-ported from branch feature-vnc--3.1 (fixes: #12416) 2008-07-15 Laszlo Attila Toth * lib/zorp/nfconnmark-kernel.h: deleted (fixes #13102) * lib/zorp/Makefile.am: removed nfconnmark-kernel.h (fixes #13102) 2008-07-15 SZALAY Attila * scripts/gen-xml-database.py, scripts/gen-zms_database.sh: Removed references to VBuster proxy. (fixes: #13461) * scripts/xmlparts/servicetnull.xml: Removed VBuster plugin minimal config because it is not used. (fixes: #13461) * debian/rules.in-pro, lib/pypolicy.c, zorp/logtags.txt: Removed vbuster name. (fixes: #13461) * debian/rules.in-pro: Removed zorp-pro-modules-rdp cration chunks. (fixes: #nobug) 2008-07-07 Balazs Scheidler * Added source marks to mark non-GPL code. Zorp GPL 3.3 features program stacking, but remote stacking is still not released. 2008-06-23 Balazs Scheidler * zorp/main.c: added "VirusBuster Antivirus Gateway" as accepted product name (fixes: #13558) 2008-06-19 SZALAY Attila * lib/proxystack.c (z_proxy_stack_remote_handshake): Check if z_stream_connector_new return with NULL. (fixes: #14077) 2008-06-11 Laszlo Attila Toth * lib/dispatch.c (z_dispatch_new_listener): sets ZSF_TRANSPARENT socket flag if the listener is transparent (fixes #14029) * lib/tpsocket.c: removed duplicated #define of IP_FREBIND (z_do_tp40_bind): sets ZSF_TRANSPARENT if the listener is transparent (fixes #14029) 2008-06-10 Szalay Attila * zorp/main.c (main): Fixed Shell Control Box license version. (fixes: #nobug) 2008-05-19 Balazs Scheidler * lib/proxy.c: removed public declaration of z_proxy_propagate_channel_props, moved functions up and down to avoid forward declarations, this fixes a compilation error * libproxy/transfer2.c: use z_proxy_loop_iteration instead of z_proxy_propagate_channel_props, * modules/sqlnet/sqlnet.c (sqlnet_main): -"- * modules/nntp/nntp.c (nntp_main): fixed negated call to z_proxy_loop_iteration 2008-05-07 Szalay Attila * lib/proxygroup.c (z_proxy_group_iteration): Changed if statement to match the z_proxy_group_thread_func statement. (fixes: #13685) (z_proxy_group_orphan): Wake up the proxygroup poll if the python part is exited. (fixes: #13685) 2008-04-28 Szalay Attila * lib/proxygroup.c (z_proxy_group_iteration): Check for alive sessions because it is possible that the only proxy in this group is stopped above cause an infinite waiting. (fixes: #13685) 2008-04-24 Balazs Scheidler * pylib/Zorp/Domain.py: fixed address parsing in case there's no mask value, reasons are too difficult to explain here, see the bugreport (fixes: #13694) 2008-04-17 Balazs Scheidler * lib/proxy.c (proxy_hash): renamed from proxy_list, (z_proxy_loop_iteration): renamed from z_proxy_update_info, (z_proxy_wakeup_method, z_proxy_wakeup): new virtual function, wakes up a proxy from an external thread * modules/ssh/ssh.c (ssh_wakeup): new function, implements the z_proxy_wakeup virtual function to wake up the proxy 2008-04-17 Laszlo Attila Toth * lib/zorp/proxy.h, lib/zorp.c: Added functions for tracks proxy sessions (threads) in the hash map Added z_proxy_update_info(ZProxy*). It calls z_proxy_propagate_channel_props then checks for the stop reqeuest flag, and logs the stop request. (fixes #13564) * zorp/main.c (main): initializes/deinitializes the proxy session list subsystem (fixes #13564) * zorpctl/main.c: new parameters: stop-session zorp_instance/proxy_session_id and its functions: z_process_stop_session and z_pcmd_stop_session (fixes #13564) * zorpctl/szig.c, zorpctl/szig.h: new function: z_szig_stop_session (fixes #13564) * lib/szig.c (z_szig_handle_command): added new command, STOPSESSION (fixes #13564) * modules/nntp/nntp.c, modules/lp/lp.c, modules/ldap/ldap.c, modules/ftp/ftp.c, modules/http/http.c, modules/imap/imap.c, modules/finger/finger.c, modules/pssl2/pssl.c, modules/smtp/smtp.c, modules/sqlnet/sqlnet.c, modules/telnet/telnet.c, modules/whois/whois.c, modules/pop3/pop3.c, modules/rsh/rsh.c, modules/tftp/tftp.c, modules/pssl/pssl.c, modules/ssh/ssh.c, modules/msrpc/msrpc.c: the proxy's main function calls z_proxy_update_info instead of z_proxy_propagate_channel_props and if it returns FALSE, terminates the proxy (fixes #13564) 2008-04-16 Laszlo Attila Toth * lib/pyattach.c, lib/pydispatch.c, lib/pysatyr.c, lib/pystream.c, lib/pyzasauth.c, modules/pssl2/psslpolicy.c, lib/pydict.c, lib/pystruct.c: Replacing PyMem_DEL, PyObject_DEL with PyObject_Del, and PyObject_NEW with PyObject_New, also it works with Python 2.5 2008-04-10 Szalay Attila * VERSION: Bumped to version 3.3.1a * lib/pybalance.c, lib/pypolicy.c, /modules/rdp/rdp_policy.c: Fixed some compilation warning and error. (fixes: #13579) * Forward ported patches from version 3.1 (694-694) 2008-03-26 Szalay Attila * VERSION: Bumped to 3.3.1 2008-03-21 Szalay Attila * VERSION: Bumped to 3.3.0.3 * Forward-ported patches from version 3.1 (678-687) 2008-03-12 Laszlo Attila Toth * lib/pystruct.c (z_policy_struct_module_init): copy z_policy_struct_types[Z_PST_NONE] on the first run, when no policy loaded yet. On policy reload it is called again but it won't refill the structure with default values (fixes #13383) 2008-03-12 Szalay Attila * modules/pssl2/pssl.c (pssl_proxy_free): Freed plugsession to avoid leaks. (fixes: #13340) * lib/proxygroup.c (z_proxy_group_thread_func, z_proxy_group_start_thread): Removed thread syncronization between proxy group thread and starter thread. (fixes: #13241) (z_proxy_group_start_session): Added checking of poll existance because of not syncronized startup. (fixes: #13241) * makeconfig.sh: removed vbuster proxy from local installs. (fixes: #13273) * tests/functional/http/transfer/mime-stacked-content-length.tests: Removed testcases which used VBuster proxy. (fixes: #13273) * tests/functional/mime/transfer/mimevirus.tests: Changed stacked proxy from VBuster to Plug. (fixes: #13273) 2008-03-06 Pal Tamas * debian/zorp-pro.postinst.in: shell script no longer dies, when licenseinstaller script returns non-0. 2008-03-06 Szalay Attila * Forward-ported patches from version 3.1 (641-677) 2008-02-28 Szalay Attila * pylib/Zorp/NAT.py (class BalanceNAT): Fixed typos in documentation. (fixes: #nobug) 2008-02-25 Szalay Attila * tests/functional/sqlnet/redirect.tests: Follow target_address_inband name changes. (fixes: #13252) 2008-02-20 Szalay Attila * modules/pssl2/pssl.c (pssl_main): Do not try to check server side certificate if server side not need ssl. (fixes: #13212) * zorp/main.c (main): Only try to setuid to zorp when run as root. (fixes: #nobug) 2008-02-18 Laszlo Attila Toth * lib/pycore.c: z_py_set_connmark is independent from TProxy since it only raises an exception. z_py_set_mark uses the correct value of SO_MARK * lib/pycore.c: New function: z_py_set_mark which is setMark() in Python code. It sets SO_MARK on the socket to the value specified by the second parameter. * lib/zorp/Makefile.am: added linebalance.h * zorpaddr/ifcfg.c: replacing g_hash_table_remove_all since it is unsupported prior to glib-2.12 * zorpaddr/cfg.h, zorpaddr/stats.h, lib/zorp/linebalance.h: instead of glib.h the required headers included from the glib directory. * zorpaddr/ifcfg.c (z_ifcfg_clear_cb): parameters marked as unused 2008-02-18 Szalay Attila * lib/pysockaddr.c (z_policy_sockaddr_inet_new_instance): Fixed python error handling. (fixes: #7174) * pylib/Zorp/NAT.py: Fixed some typos prevented python code to compile or run. (fixes: #7174) 2008-02-18 Laszlo Attila Toth * pylib/Zorp/NAT.py: fixing imports. * debian/zorp-pro.files.in: added zorpaddr to the list. * zorpaddr/zshmem.c (z_shmem_validate): always set shmem size; fixing typo. * zorpaddr/main.c: using same parameter scheme as in zorp * lib/zorp/linebalance.h: using guint32 for _ZorpBalancePolicyInterface.ip addr as in the kernel, and it is big (network) endian. * lib/pybalance.c (z_py_zorp_balance_get_chances): convert ip address to host endian. * pylib/Zorp/NAT.py (BalanceNAT): a break statement was missing * zorpaddr/cfg.c (z_cfg_parse_iface): The function can get empty interface name which is valid. * zorpaddr/main.c (z_zorpaddr_main_loop): initialize pointer to null * pylib/Zorp/NAT.py: add import random.SystemRandom * pylib/Zorp/NAT.py: If keep_sessions is on, store the correct address. * pylib/Zorp/NAT.py (BalanceNAT): remove end of old line in previous patch * lib/ifmonitor.c: struct ZIfaceInfo.flags is guint32 as in the kernel. Added z_ifmon_get_iface_flags() to get this flag of an interface specified by ifindex. * lib/zorp/ifmonitor.h: Added z_ifmon_get_iface_flags() * zorpaddr/cfg.c, zorpaddr/main.c, zorpaddr/zorpaddr.h, zorpaddr/zshmem.c: code cleanup. Remove empty lines. Remove spaces from end of lines. Change tabs to spaces. * zorpaddr/ifcfg.c: New functions z_ifcfg_update_group_preferences(), z_ifcfg_update_group_preference() to calculate the real preference used by z_stats_update(). Code cleanup. * zorpaddr/ifcfg.h: Added Z_IFCFG_UP status (iface is up and has an IP address), and Z_IFCFG_LIVE for later usage. struct _ZorpIfaceData has 3 different preference (percent) values. Code cleanup. * zorpaddr/stats.c: z_stats_update_prefs() using real_pref member of struct _ZorpIfaceData. Code cleanup. * zorpaddr/stats.c (z_stats_update): multiply the calculated preference with the interface count of the current group. The sum of prefs is nearly 100% in every case. * zorpaddr/ping.c: pinging thread's main function and communication with the main thread of the program (fixes #6647) * zorpaddr/ping.h: Ping thread init/destroy functions and data type for communication (fixes #6647) * zorpaddr/cfg.c: added host parsing - used by pinging thread; new function: z_cfg_parse_hosts (fixes #6647) * zorpaddr/ifcfg.c: cleanup and using info specified by pinging thread. * zorpaddr/ifcfg.h: added host list for ZorpAddrGroup, status for ZorpIfaceData. (fixes #6647) code cleanup and comments. * zorpaddr/main.c: Managing pinging thread (fixes #6647). code cleanup. * zorpaddr/stats.c: code cleanup. * zorpaddr/zshmem.c: code cleanup. * zorpaddr/Makefile.am: added ping.c and ping.h (fixes #6647) * zorpaddr/zorpaddr.xml.sample: added optional host element to the groups (fixes #6647) * lib/zorp/linebalance.h: cleanup: modified "constant" names (added Z_LB_ prefix) * lib/pybalance.c: cleanup: using the new constants * zorpaddr/zorpaddr.xml.sample: renamed to zorpaddr/zorpaddr.cfg.sample * zorpaddr/Makefile.am: changed config file name * zorpaddr/cfg.c (z_cfg_parse_iface): If a group contains the same interface more than once, the preference added each time. * zorpaddr/ifcfg.c: z_ifcfg_iface_watch: updatedata hasn't got index member any more. Indentation changes. (z_ifcfg_add_and_get_iface_data, z_ifcfg_update_group_preference): guint is used for loop variables as in ZorpAddrData. * zorpaddr/ifcfg.h: enum zifcfgstatus got another member, Z_IFCFG_PING which is used if a raw socket can be set up for pinging. (ZorpAddrInterface, ZorpIfaceData): removed ping_index member (ZorpAddrInterface): type guint32 is used for *_num members. * zorpaddr/main.c: zorpaddr.cfg is the config file's default name. z_zorpaddr_main_loop, main: z_ping_destroy_and_wait renamed to z_ping_destroy as in ping.c. * zorpaddr/zshmem.c (z_shmem_copy_data): using guint32 for loop variables. * zorpaddr/ping.c: z_ping_destroy_and_wait became z_ping_destroy and z_ping_destroy is z_ping_destroy_and_nowait. Comments added. The global variables are at one place. Functions for ping sending, receiving; updating statics. If a host doesn't send an ICMP echo reply packet within 10 seconds, the thread assumes it is down. If all hosts are inaccessible, the interface is virtually down (its status' Z_IFCFG_LIVE bit is unset. * zorpaddr/ping.h (struct ZPingUpdataData): removed index member. * zorpaddr/stats.c (z_stats_update_prefs): using guint32 for loop variables. * zorpaddr/stats.c (z_stats_update_prefs): only speed of available interfaces (marked as Z_IFCFG_LIVE) are used at preference calculation * zorpaddr/ping.c: diff_time remove infinite loop. (z_ping_update_stats): parameter is not needed, more logs. The status of the config's ZorpIfaceData members is modified. z_ping_thread_main_func: always updating statistics . * zorpaddr/ping.c (z_ping_update_stats): modified logging. Interface changes logged only if they really changed. (z_ping_thread_main_func): more unambigous logging. poll() timeout is now 0.1 second. * zorpaddr/ifcfg.c, zorpaddr/ifcfg.h, zorpaddr/stats.c: renaming Z_IFCFG_LIVE to Z_IFCFG_ALIVE * zorpaddr/ping.c: renaming Z_IFCFG_LIVE to Z_IFCFG_ALIVE; (z_ping_update_stats): more obvious variable names; logging only if pingable hosts' count change: 0 <-> !0 * zorpaddr/main.c (main): change default loglevel to 3 as in zorp/main.c. * zorpaddr/ping.c (z_ping_update_stats): if the current group's host_num is 0, also there is no host to ping, skip remaining code. (z_ping_event_add): changed ZorpIfaceData.status: if there is a pinger socket, the interface in the group is not alive but can send ping. If the socket bind() failed, on the contrary: the status' ALIVE bit is set and PING is unset. (z_ping_init): thread name changed to 'pinger_thread' * zorpaddr/stats.c (z_stats_update_prefs): logging the preference in shared memory (group, iface, pref). * zorpaddr/cfg.c: checking the configuration file during loading. Check-only mode added when the actual configuration is not modified. Removed GError ** parameters. * zorpaddr/cfg.h: removing GError parameters from fhe functions. Added: z_cfg_check(cfg file). * zorpaddr/ifcfg.c: added: z_ifcfg_update_all_group_preferences to update preferences at once * zorpaddr/main.c: The code of the config reloading is commented out. * zorpaddr/stats.c: Modified stats calculation. * zorpaddr/cfg.c (struct ZCfgOpts): added comments. (z_cfg_parser_cb): clears and frees opts->iface_names to prevent memory leak. (z_cfg_reload): simplier reloading mechanism + comments. Code cleanup (remove unecessary empty lines and trailing spaces). * zorpaddr/ifcfg.c: mutex is removed. Added tmp_interfaces to hold previous interfaces and their statistics. (z_ifcfg_get_iface): at config reloading also check tmp_interfaces, and if an interface is also used in the new config, move it to the new interfaces hash table. New functions: z_ifcfg_reload_{start,finish}() used at config reload. Removed: z_ifcfg_reload() * zorpaddr/ifcfg.h: new functions: z_ifcfg_reload_{start,finish} used at config reload. * zorpaddr/main.c (z_zorpaddr_main_loop): uncomment config reloading code. * zorpaddr/stats.c (z_stats_update_prefs): if there are no active interfaces, pref_speed_sum may be 0. Checking it. Code cleanup (remove unecessary empty lines and trailing spaces). * zorpaddr/zshmem.c (z_shmem_reload): clears shared memory. Code cleanup (remove unecessary empty lines and trailing spaces). * zorpaddr/ping.c: removed unnecessary lines. * zorpaddr/cfg.{c,h}: Removed parameters of z_cfg_reload() * zorpaddr/ifcfg.c: (z_ifcfg_update_cb): added extra check for null pointer. Added z_ifcfg_reload_cb() to iterate through all interfaces and send an 'ADD' event to the pinger thread if the interface is up and has an IP addess. This code is called from z_ifcfg_reload_finish() if its parameter is TRUE * zorpaddr/ifcfg.h (z_ifcfg_reload_finish): a boolean parameter is added * zorpaddr/main.c (z_zorpaddr_main_loop): call of z_ifcfg_update() is allways successful, its check is removed. * pylib/Zorp/NAT.py (BalanceNAT.performTranslation): If all preference is zero, raise a LimitException * zorpaddr/cfg.c (z_cfg_parse_hosts): ignore empty host names * zorpaddr/ifcfg.c (z_ifcfg_update_cb): If the interface is valid and has an IPv4 address, add it to the pinger thread (z_ifcfg_reload_start): don't free key of the hash table (z_ifcfg_reload_finish): before destroying tmp_interfaces, set it to null (and using temporal variable) * zorpaddr/main.c (z_zorpaddr_main_loop): update ifcfg after pinger thread initialized. * zorpaddr/ping.c (z_ping_update_stats): if the interface is down, log it only once * zorpaddr/Makefile.am: added header files as sources. * lib/ifmonitor.c (z_ifmon_change_iface_addr): primary IP address is always the first in the list (in4_addresses[0]). * Makefile.am: compiling zorpaddr * zorpaddr/ifcfg.c (z_ifcfg_iface_watch): set address if the interface index is already set (z_ifcfg_grp_add_iface): ifindex, primary address is unnecessary here (z_ifcfg_set_ip_address): validating shmem structure because IP address update is rare. z_ifcfg_update: new function to set interface indices * zorpaddr/ifcfg.h (ZorpAddrInterface): addedd status member to indicate if the if_index member is set or yet unset. * zorpaddr/main.c (z_zorpaddr_main_loop): calls z_ifcfg_update if an inteface index is not yet set. * lib/ifmonitor.c and lib/zorp/ifmonitor.h: added functions for get primary address (currently IPv4 only) and index of an interface. * zorpaddr/ifcfg.c: using the new functions. * zorpaddr/ifcfg.h (ZorpAddrInterface): added if_index (interface index) * zorpaddr/main.c: removed unnecessary blank lines. The daemon goes to background. * zoraddr/zshmem.c (z_shmem_destroy): invalidating shared memory data * zorpadrr directory: Implementation of Line Balancer Daemon, ZorpAddr * lib/zorp/linebalance.h: structures and constants * lib/zorp/policy.h: removed duplicated define line * lib/pybalance.c: using new, fixed structure (ZorpBalanceShmemData) It is the representation of the used shared memory 2008-02-18 Szalay Attila * lib/Makefile.am: Fixed compilation problem caused by an invalid separator. * lib/pypolicy.c (z_policy_boot): Added balancer initialization. (fixes: #7174) * lib/pysockaddr.c (z_policy_sockaddr_inet_new_instance): Added the pocibility to create SockAddr from ip number. Used by BalanceNAT. (fixes: #7174) * pylib/Zorp/NAT.py (class BalanceNAT): Added new class which implement lineBalance NAT. (fixes: #7174) * lib/pybalance.c : New file to implement LineBalance C part. (fixes: #7174) 2008-02-08 Szalay Attila * VERSION: Bumped to 3.3.0.2 2008-02-04 Szalay Attila * debian/zorp-pro.postinst.in: Added the possibility to install license. (fixes: #13056) 2008-02-01 Szalay Attila * tests/python/test_authorization.py: Fixed unit test to follow changes in code. (fixes: #nobug) * tests/unit/Makefile.am: Removed test_base64, test_codegzip and test_codecipher tests, because the tested code has been moved to zorp-lib. (fixes: #nobug) 2008-01-31 Szalay Attila * VERSION: Bumped to version 3.3.0.1 2008-01-24 Simon Gabor * modules/rdp/Rdp.py: declaration comment of host_key_rsa_file in the internal python doc fixed (fixes: #12902) 2008-01-24 Szalay Attila * modules/telnet/Telnet.py (class AbstractTelnetProxy): Added enable_audit documentation. (fixes: #12920) * lib/code.c, lib/code_base64.c, lib/code_cipher.c, lib/code_gzip.c, lib/zorp/code.h, lib/zorp/code_base64.h, lib/zorp/code_cipher.h, lib/zorp/code_gzip.h: Moved this file into libzorpll. (fixes: #12253) 2008-01-24 Szalay Attila * modules/rdp/debian/Makefile.am (EXTRA_DIST): Follow the zorp-pro-module-rdp.files file rename. (fixes: #12957) 2008-01-20 SZALAY Attila * Forward-ported patches from version 3.1 (594-640) 2008-01-19 Balazs Scheidler * lib/notification.c (z_notify_proxy_context_add_params): use z_proxy_get_addresses_locked, (z_notify_event_send): removed locking, it is provided by caller functions, fixed reference leak on notify_fn, (z_notify_event_policy): added an additional mutex to protect notification_thread (fixes: #12746) (z_notify_event_va): -"- * lib/proxy.c (z_proxy_get_addresses_locked): renamed from z_proxy_get_addresses, removed locking, (z_proxy_get_addresses): new function, a locking wrapper around z_proxy_get_addresses_locked 2008-01-10 Szalay Attila * debian/control.in-pro: Removed zorp-pro-module-rdp package. (fixes: #12957) * modules/rdp/debian/zorp-pro-modules.files: Renamed from zorp-pro-module-rdp.files to merge rdp into zorp-pro-modules package. (fixes: #12957) 2008-01-03 Simon Gabor * modules/telnet/telnet.[hc]: typo fixed at log facility name 'telnet.violation' (fixes: #11662) 2007-12-19 Szalay Attila * modules/mime/mimedata.c (mime_transfer_dst_shutdown): Fixed a compilation problem. (fixes: #8787) 2007-12-19 Szalay Attila * modules/mime/mimedata.c (mime_transfer_dst_shutdown): Drop rejected attachment if silent_drop is true. (fixes: #8787) 2007-12-19 Szalay Attila * modules/mime/mime.c (mime_config_set_defaults): Changed default value of silent_drop to FALSE. (fixes: #8787) * modules/mime/Mime.py (class AbstractMimeProxy): Changed silent_drop documentation. (fixes: #8787) 2007-12-19 Fekete Robert * *.*py: Added some type definitions. (fixes: #12504) 2007-12-09 Balazs Scheidler * modules/pssl2/pssl.c (pssl_config_set_defaults): set server_check_subject to TRUE by default (fixes: #12692) 2007-12-09 Szalay Attila * modules/pop3/pop3.c, modules/pop3/pop3.h, modules/pop3/pop3cmd.c: Changed log message about reply messages from pop3.reply to pop3.response. (fixes: #11667) 2007-12-09 Pal Tamas * debian/control.in-pro: python2.3-pyopenssl dependency changed to python-pyopenssl. (fixes: #12820) 2007-12-09 Simon Gabor * modules/telnet/telnet.c, modules/rdp/rdp.c: leftover references to z_policy_dict_free fixed (fixes: #12502) 2007-12-09 olek * configure.in.in : change PYTHON_MIN_VERSION from 2.3 to 2.4 * debian/control.in-pro : change depend zorp-pro, from python2.3 to python2.4 2007-11-13 Simon Gabor * modules/nntp/nntp.[hc], nntpcmd.c: NNTP_REPLY renamed to NNTP_RESPONSE, duplicate defines removed (fixes: #11666) * modules/imap/imap.[hc], imapcmd.c: IMAP_REPLY renamed to IMAP_RESPONSE (fixes: #11665) 2007-11-13 Szalay Attila * modules/ftp/ftp.h: Changed reply log message to response. (fixes: #11664) * modules/ftp/ftp.c (ftp_answer_parse): Changed reply log message to response. (fixes: #11664) 2007-11-13 Pal Tamas * debian/control.in-pro: Added proper python-kzorp virtual package support. 2007-11-13 Szalay Attila * lib/audit.c (z_audit_trail_new): Changed audit trail file name to .zat. (fixes: #12457) 2007-10-08 Szalay Attila * debian/control.in-pro, debian/rules.in-pro: Fixed python-kzorp package name when building with binary-branch. (fixes: #nobug) 2007-10-02 Szalay Attila * modules/ssh/sshsftp.c (ZProxyFuncs ssh_sftp_proxy_funcs): Fixed compilation problemcaused by the previous patch. (fixes: #nobug) * lib/pyproxy.c (struct _ZPolicyProxy, z_policy_proxy_bind_implementation): Fixed compilation problems caused by the previous patch. (fixes: #nobug) 2007-09-29 SZALAY Attila * Forward-ported patches from version 3.1 (538-593) 2007-09-29 Szalay Attila * modules/pssl2/pssl.c: Changed to text representation of side in log messages. (fixes: #12321) * configure.in.in, lib/audit.c, lib/proxy.c, lib/pycore.c, lib/pyproxy.c, lib/pysatyr.c, lib/pyzasauth.c, lib/zorp.c, modules/imap/imap.c, modules/ldap/ldap.c, modules/lp/lp.c, modules/mime/mime.c, modules/msrpc/msrpc.c, modules/nntp/nntp.c, modules/pop3/pop3.c, modules/pssl2/pssl.c, modules/radius/radius.c, modules/rdp/rdp.c, modules/rsh/rsh.c, modules/sip/sip.c, modules/smtp/smtp.c, modules/sqlnet/sqlnet.c, modules/ssh/ssh.c, modules/tftp/tftp.c, modules/vbuster4/vbuster.c, zorp/main.c: Adapted to the changes in zorp-lib-license. (fixes: #11634) 2007-09-28 Balazs Scheidler * lib/tpsocket.c (z_do_tp40_bind): added support for IP_TRANSPARENT while falling back to IP_FREEBIND if the first is not defined 2007-07-11 Szalay Attila * debian/control.in-pro: Fixed some build dependency problem. (fixes: #nobug) 2007-07-09 Szalay Attila * VERSION: Bumped to 3.3alpha0.1 2007-07-02 MOLDVAI Dezso E. * pylib/Zorp/Chainer.py: XML documentation validity fixes (fixes: #nobug) 2007-07-02 Krisztian Kovacs * pylib/kznf/kznf/kznfnetlink.py: add new message and attribute type constants (create_query-msg): new function to construct a query message (fixes: #nobug) 2007-07-02 Balazs Scheidler * zorp/main.c: enable log-tags by default 2007-07-02 Balazs Scheidler * Forward-ported patches (528-537) from 3.1 2007-06-18 Szalay Attila * Forward-ported patches (513-527) from 3.1 * Forward-ported patches (501-512) from 3.1. * Forward-ported patches (487-500) from 3.1. 2007-03-28 Pfeiffer Szilard * VERSION: Initial version number change. (fixes: #nobug) * configure.in.in: Fixed library version checking. (fixes: #nobug) 2007-02-22 Szalay Attila * Forward-ported patches (440-486) from 3.1. (fixes: #nobug) 2007-02-22 Krisztian Kovacs * pylib/Zorp/Zone.py (InetZone.buildKZorpMessage): fix KZF_ZONE_UMBRELLA reference, it's in the kznf.kznfnetlink namespace (fixes: #11068) * modules/ssh/sshpolicy.c (ssh_policy_query_channel_specific): use z_policy_dict_destroy() instead of _dict_free() that does not exist in 3.2 (fixes: #nobug) * lib/proxy.h (ZProxy): add channel_props_set[EP_MAX] array, channel_props_set[side] is TRUE if the channel properties have been actually set on the fd (fixes: #10935) * lib/proxy.c (z_proxy_connect_server): make sure to propagate channel properties before and after connecting (fixes: #10935) (z_proxy_user_authenticated): remove mismerged z_proxy__propagete_channel_props() call (fixes: #10935) (z_proxy_propagate_channel_props): separate propagating the ToS value and setting the fd ToS, props[side].tos[IN] is now propagated to OUT of the other side, do not return TRUE as this function is declared void (fixes: #10935) 2007-01-08 Balazs Scheidler * pylib/Zorp/KZorp.py: added missing 'socket' import * modules/http/http.c (http_handle_connect): removed uninitialized use of the rc variable, which is not needed anyway, fixes a possible ABORT on the processing of CONNECT request 2007-01-08 Balazs Scheidler * VERSION: bumped to 3.2.3 * pylib/Zorp/KZorp.py (startTransaction): handle ECONNREFUSED as it might also indicate missing KZorp and causes Zorp to start up slowly (which caused problems in ZTS) * lib/proxy.c (z_proxy_set_priority): new function, sets proxy priority, (z_proxy_propagate_channel_props): added DSCP mapping and setSessionPriority callback support (fixes: #10643) 2006-12-18 Krisztian Kovacs * VERSION: bumped to 3.2.2 * lib/pypolicy.c (z_policy_cleanup): new function, called when shutting down Zorp, calls Zorp.cleanup with the NET_ADMIN capability held (fixes: #10265) * lib/zorp.c (z_main_loop): call policy deinit and cleanup when shutting down (fixes: #10265) * pylib/Zorp/KZorp.py: move helper functions up one level, necessary to implement flushKZorpConfig() (fixes: #10265) (flushKZorpConfig): flush KZorp dispatchers and services (fixes: #10265) * pylib/Zorp/Zorp.py (cleanup): new callback called when shutting down Zorp, cleans up the in-kernel KZorp objects of the instance (fixes: #10265) * lib/proxygroup.c (z_proxy_group_unref): free the poll object allocated in z_proxy_group_thread_func (fixes: #nobug) 2006-12-18 Pal Tamas * pylib/kznf/Makefile.am: local install of kznfnetlink now honors configure's --with-python option, instead of runnig with the default python binary. * debian/rules.in-pro: Builder builds python-kzorp with the minimal required python binary. * debian/control.in-pro: Source package now Build-Depends on the proper python@PYTHON_MIN_VERSION@-dev. 2006-12-18 Balazs Scheidler * forward ported patches from 3.1, synced to zorp 3.1.8 2006-11-17 Balazs Scheidler * lib/pyproxy.c (z_policy_proxy_bind_implementation): don't start a new ZProxy instance if self->proxy is already set (might happen when a z_proxy_group_start_session fails) (fixes: #10554) 2006-10-30 SZALAY Attila * VERSION: Bumped to 3.2.1 2006-10-27 SZALAY Attila * VERSION: Bumped to 3.2.0.3 2006-10-27 Krisztian Kovacs * pylib/Zorp/Zorp.py (init): don't specify exception type for except (fixes: #nobug) * pylib/Zorp/KZorp.py (downloadKZorpConfig.startTransaction): use random() instead of randing() as wait is no longer an integer (fixes: #nobug) * pylib/Zorp/KZorp.py (downloadKZorpConfig): fix indentation problem in zone traversal code (fixes: #10354) * pylib/kznf/kznf/nfnetlink.py (Handle.close): new method, closes the netlink socket (fixes: #10354) * pylib/Zorp/KZorp.py (downloadKZorpConfig): run all transactions inside a try-except block and close nfnetlink handle if an exception is caught (fixes: #10354) * pylib/Zorp/KZorp.py (downloadKZorpConfig.exchangeMessage): put nfnetlink talk() result into the exception message (fixes: #nobug) (downloadKZorpConfig.startTransaction): change initial wait interwal length to 0.1 second and retry limit to 7, this way retries won't take more than 0.1 * 2^6 = 6.4 seconds (fixes: #nobug) * pylib/Zorp/Dispatch.py (ZoneDispatcher.buildKZorpMessage): do not use super() as it works only for new-style classes, fix typo in kznfnetlink function name and services hash reference (fixes: #10353) (CSZoneDispatcher.buildKZorpMessage): do not use super(), fix kznfnetlink function name type and services hash reference (fixes: #10353) * pylib/Zorp/Zorp.py (init): catch and log exception contents and traceback instead of simply swallowing it (fixes: #nobug) 2006-10-19 Krisztian Kovacs * lib/plugsession.c (z_plug_update_eof_mask): call the ->finish() callback of the plugsession as the last operation of the function. First of all, we must be sure to have all of the streams removed from the poll. Other than that, ->finish() will probably free the session (it's not reference counted), so it's absolutely forbidden to do anything with the session after having called ->finish(). Every caller of z_plug_update_eof_mask() does so as the last operation before returning from the I/O callback, so moving the call to the end of this function is supposed to solve the problem. (fixes: #10240) * lib/proxygroup.c (z_proxy_group_iteration): really append proxy pointer to self->nonblocking_proxies (fixes: #10237) * lib/plugsession.c (z_plug_copy_data): don't copy more than MAX_READ_AT_A_TIME packets at a time (fixes: #10237) * pylib/Zorp/Service.py (PFService.__init__): make router argument non-mandatory, defaults to the global default_router or TransparentRouter if no global default was configured (fixes: #10224) * pylib/kznf/kznf/kznfnetlink.py: introduce KZF_SVC_FORGE_ADDR service flag (fixes: #10225) * pylib/Zorp/Service.py (PFService.buildKZorpMessage): set KZF_SVC_FORGE_ADDR flag iff forge_addr is enabled in the router (fixes: #10225) * pylib/Zorp/Chainer.py (ConnectChainer): fix default value of timeout_connect argument, now all descendant classes use None as the default argument value and the constructor of ConnectChainer uses the value set in Config.py as default (fixes: #10235) (StateBasedChainer) the timeout_state value is now in msecs, the default has been changed (fixes: #10235) (FailoverChainer) swapped the order of timeout and timeout_state arguments for compatibility, this was necessary because timeout_state is now in milliseconds (fixes: #10235) * lib/proxy.c (z_proxy_propagate_channel_props): add missing z_enter() and z_leave() macreos (fixes: #nobug) 2006-10-11 Krisztian Kovacs * pylib/Zorp/NAT.py (GeneralNAT.getKZorpMapping): domain.netaddr() and domain.broadcast() methods return addresses in network byte order, so conversion to host byte order is necessary before passing these values to kznfnetlink (fixes: #nobug) 2006-10-11 Balazs Scheidler * lib/pydispatch.c (z_policy_dispatch_new_instance_iface_group): the string value for group might also contain a number handle that as well. 2006-10-11 Krisztian Kovacs * pylib/Zorp/Zone.py (InetZone.buildKZorpmessage): don't check if the given service name is present in the current service (fixes: #10195) 2006-10-11 Balazs Scheidler * lib/pydispatch.c(z_policy_dispatch_bind_new_instance_iface_group): use dual-typing for the group parameter and resolve it if it was a string * lib/dgram.c (z_nf_origaddrs_opt): initialize to -1, (z_nf_dgram_socket_setup): don't enable SO_RECVORIGADDRS if z_nf_origaddrs_opt is unset, (z_dgram_init): don't initialize z_nf_origaddrs_opt in the Z_SD_TPROXY_NETFILTER_V40 case * configure.in.in: removed various tproxy fallback options, they'll always be enabled with ENABLE_NETFILTER_TPROXY * lib/dgram.c (z_dgram_init): added case for Z_SD_TPROXY_NETFILTER_V40 * lib/sysdep.c (z_sysdep_parse_tproxy_arg): removed complicated preprocessor conditionals, added tproxy40, (z_sysdep_init): added tproxy40 detection (basically it's hardwired as there's no way to properly autodetect it) * lib/tpsocket.c: removed complicated preprocessor conditionals, added tproxy 4.0 support * zorp/main.c (z_version): removed publishing tproxy fallback options as they don't exist anymore 2006-10-11 Krisztian Kovacs * pylib/kznf/kznf/kznfnetlink.py (parse_bind_{addr,iface,ifgroup}_attr): fix syntax errors (fixes: #nobug) * pylib/Zorp/Dispatch.py (Dispatcher.buildKZorpMessage): pass rule_port instead of self.rule_port to kznfnetlink message builder (fixes: #nobug) * pylib/Zorp/KZorp.py: fix kznfnetlink module references introduced in zorp-core--feature-kzorp--3.2--patch-60 (fixes: #10052) * pylib/Zorp/KZorp.py (startTransaction): resend KZNL_MSG_START message in case KZorp returns an error (fixes: #10052) 2006-10-11 Pal Tamas * pylib/kznf: Added directory. * pylib/kznf/Makefile.am: Added Makefile template. Contains clean and distclean targets and the setup.py as EXTRA_DIST. * pylib/kznf/setup.py: Added python script to allow installing the kznf module in a python friendly way. * debian/rules.in-pro: Added instruction to install python-kzorp to the proper place. * configure.in.in: Added Makefiles under pylib/kznf to AC_OUTPUT. * pylib/Makefile.am: Added directory kznf to SUBDIRS. * pylib/Zorp/Makefile.am: Removed Lib from SUBDIRS. * pylib/Zorp/NAT.py, pylib/Zorp/Service.py, pylib/Zorp/Zone.py, pylib/Zorp/KZorp.py, pylib/Zorp/Dispatch.py: Changed all Lib.kznfnetlink calls to kznf.kznfnetlink. * debian/control.in-pro: Added package python-kznf. zorp-pro noww depends on this package. * debian/zorp-pro.files.in: Removed all knfnetlink files. * pylib/kznl/kznl/Makefile.am: Modifed, only EXTRA_DIST remains. * pylib/Zorp/Lib: directory moved to pylib/kznf/kznf 2006-10-11 Attila SZALAY * VERSION: Bumped to version 3.2.0.2 2006-10-11 Balazs Scheidler * lib/proxygroup.c (z_proxy_group_start_thread): add a variable in addition to a condvar to synchronize thread startup * modules/pssl2/pssl.c (pssl_finished): new function, calls z_poll_quit, (pssl_main): exit the proxy main loop when z_poll_quit() was called * modules/pssl2/pssl.c (pssl_app_verify_cb): don't use SSL_app_data, use user_data instead, (pssl_start_main_session): removed, (pssl_main): parts of the old pssl_start_main_session moved here, (pssl_proxy_free): z_poll_unref handles NULL args, so no need to explicitly check for that * pylib/Zorp/NAT.py (NATPolicy.performTranslation): never clone SockAddrs with wildcard set to TRUE * modules/sip/Sip.py (SipProxy.rewriteAddr): adapted to latest NAT changes * modules/http/http.c (http_handle_connect): connectMethod returns an instance instead of an integer, * modules/http/Http.py (AbstractHttpProxy.connectMethod): use stackProxy() to perform stacking instead of a separate implementation * pylib/Zorp/Proxy.py (Proxy.stackProxy): raise an exception if stacking failed * lib/pydict.c (ZPolicyDict): added reference counter (ZPolicyMethod): added dict member, (z_policy_method_new): added a reference to the dict instance to avoid freeing it while the "floating" method object is around, (z_policy_method_free): free reference to dict, (z_policy_dict_method_get_value): pass dict instance as argument to z_policy_method_new, (ZPolicyHash, z_policy_hash_new, z_policy_hash_free, z_policy_dict_hash_get_value): same changes as ZPolicyMethod, (ZPolicyDimHash, z_policy_dim_hash_new, z_policy_dim_hash_free, z_policy_dict_dim_hash_get_value): same changes as ZPolicyMethod, (z_policy_dict_new): added initialization of self->ref_cnt, (z_policy_dict_ref): new function, increments ref_cnt, (z_policy_dict_unref): renamed from z_policy_dict_free, added refcounting, (z_policy_dict_destroy): new function, needs to be called once for every dictionary, frees self->vars to break circular references * lib/proxy.c (z_proxy_destroy_method): use z_policy_dict_destroy instead of _free, * lib/pystruct.c (z_policy_struct_free): -"-, * modules/ssh/sshpolicy.c (ssh_policy_query): -"-, * lib/proxygroup.c: added z_enter/z_leave pairs to important functions * lib/attach.c (z_attach_cancel): check if self->connector is NULL * lib/proxygroup.c (z_proxy_group_get_context): check if self->poll is NULL and return NULL in that case * lib/pyattach.c (z_policy_attach_start_method): save a reference to conn->local * lib/proxygroup.c (z_proxy_group_start_session): added locking to protect self->sessions, (z_proxy_group_stop_session): -"-, added some locking related notes, * lib/proxystack.c (z_proxy_stack_object): check if the object returned by stackProxy() is indeed a Proxy instance, * lib/pyproxygroup.c (z_policy_proxy_group_new_instance): the "start" method references the proxy_group instance as otherwise when only the start method is referenced (and not the instance), the proxy group is freed too early * pylib/Zorp/Proxy.py (Proxy.stackProxy): return the proxy instance as that is needed by the C part, cleaned up error handling, * pylib/Zorp/Chainer.py (ConnectChainer.establishConnection): use Attach.start instead of block() as the first does not exist, (ConnectChainer.getNextTarget): target_local is not an array, (ConnectChainer.connectTarget): pass 'session' instead of 'self' as an argument to getNextTarget * lib/pystruct.c (z_policy_struct_module_init): added entry for Z_PST_PROXY_GROUP, * debian/zorp-pro.files.in: removed Attach.py * modules/plug/plug.c: removed obsolete reference to fastpath.h * modules/sip/sip.c, modules/sip/sip.h: SipProxySession merged into SipProxy, all SipProxySession references changed to SipProxy, (sip_proxy_session_*): functions removed, (sip_read_callback): use z_proxy_nonblocking_stop() to indicate that proxy is to be stopped, (sip_init_streams): renamed from sip_proxy_session_init_streams, (sip_start_main_session, sip_start_secondary_session, sip_secondary_accept, sip_enable_secondary_sessions, sip_disable_secondary_sessions, sip_purge_sessions, sip_main_loop): functions removed, (sip_register_vars): removed secondary_mask and friends, (sip_set_defaults): removed unneeded initializations, (sip_nonblocking_init, sip_nonblocking_deinit): new functions, largely constructed from existing code, (sip_proxy_free): parts of deinitialization moved here, (sip_proxy_funcs): use C99 initializers * modules/http/httpftp.c (http_ftp_initiate_passive_data): follow ZAttach changes * modules/rsh/rsh.c (rsh_connect_client_stderr): -"- * modules/radius/radiussession.c: removed this file, everything is moved back to radius.c, * modules/radius/radius.c (radius_config_set_defaults): removed secondary_mask and friends, (radius_register_vars): -"-, (radius_start_main_session, radius_start_secondary_session, radius_secondary_accept, radius_enable_secondary_sessions, radius_disable_secondary_sessions, radius_main_loop,): removed these functions, (radius_nonblocking_init, radius_nonblocking_deinit): new functions, initialize the proxy in nonblocking mode, (radius_proxy_new): specify ZPF_NONBLOCKING flag, (radius_proxy_funcs): use C99 initializers, * modules/radius/radius.h: removed unneeded declarations, merged RadiusSession into RadiusProxy * modules/radius/radiuspacket.c: use RadiusProxy instead of RadiusSession * modules/msrpc/msrpcforward.c (msrpc_forwarder_accept): follow ZAttach changes * modules/msrpc/msrpc.c (msrpc_proxy_funcs): use C99 initializers * modules/ftp/ftp.c (ftp_data_start_proxy): removed explicit ToS propagation, it'll be taken care of by the core, (ftp_proxy_funcs): use C99 initializers * modules/ftp/ftp.c (ftp_data_prepare): set aparam.timeout instead of aparam.tcp.timeout (because of ZAttach change) * modules/anypy/anypy.c, modules/finger/finger.c, modules/http/http.c, modules/imap/imap.c, modules/ldap/ldap.c, modules/lp/lp.c, modules/mime/mime.c, modules/pop3/pop3.c: use C99 initializers for ZProxyFuncs initialization * lib/proxy.c (z_proxy_propagate_channel_props): new function to be called every time some kind of data movement was performed (like poll loops), propagates ToS value from client->server and server->client directions, (z_proxy_config_method): added client_tos & server_tos attributes, (z_proxy_run): call z_proxy_propagate_channel_props after startup in order to be server_local_tos initialized, so that Attach can use the actual ToS value (z_proxy_connect_server): call z_proxy_propagate_channel_props after connection was successfully established, * lib/zorp/proxy.h: nonblocking_deinit returns void, added ZChannelProps structure * lib/plugsession.c (z_plug_session_free): handle self == NULL * lib/proxygroup.c (ZProxyGroup): nonblocking_start_queue member renamed from nonblocking_sessions, added nonblocking_proxies for a list of non-blocking proxies, (z_proxy_group_stop_session): maintain nonblocking_proxies by removing the ending proxy, (z_proxy_group_iteration): added z_proxy_propagate_channel_props call for nonblocking proxies, add new nonblocking proxies to nonblocking_proxies list, (z_proxy_group_unref): free nonblocking_proxies list * pylib/Zorp/Chainer.py (ConnectChainer.establishConnection): set ToS based on the client's setting * lib/proxystack.c (z_proxy_stack_remote_handshake): adapted to ZConnector changes * lib/satyr.c: removed inclusion of proxy.h * lib/attach.c (z_attach_setup_connector): removed tos parameter from ZConnector constructor, call z_connector_set_tos separately * lib/pyattach.c (z_policy_attach_new_instance): adapted to ZAttach changes * lib/zorp/dgram.h: adpated to ZConnector changes * lib/zorp/attach.h (ZAttachParams): timeout parameter is moved to the global section instead of the tcp specific params, * lib/attach.c (z_attach_setup_connector): use timeout from params.timeout instead of params.tcp.timeout * lib/pyattach.c (z_policy_attach_new_instance): support for timeout argument in a protocol independent manner * lib/satyr.c (z_satyr_connect): removed tos argument passed to z_stream_connector_new(), * lib/zasauth.c (z_zas_connect): -"- * lib/pydict.c: changed all g_assert(0) to g_assert_not_reached(), (ZPolicyDictEntry): added int8_value member, (z_policy_dict_int_parse_args): added support for Z_VT_INT8, (z_policy_dict_int_get_value): -"-, (z_policy_dict_int_set_value): -"-, * lib/pydict.c (z_policy_dict_types): added Z_VT_INT8 entry * lib/attach.c (ZAttach): removed multithreaded synchronization, z_attach_start_block() either uses the thread of the current proxy, or uses z_connector_start_block(), no other thread synchronization is necessary, (ZAttach): added proxy member, removed reference counting, (z_attach_setup_connector): new function, initializes self->connector, (z_attach_start): nonblocking start function, performs the connection operation in the context of the specified poll or by the poll of the associated proxy, (z_attach_ref, z_attach_unref): removed, (z_attach_free): new function, from the remnants of unref * lib/dispatch.c (ZDispatchCallbackFunc): renamed from ZDispatchCallback, * lib/plugsession.c (ZPlugSession): removed dict, added started members, removed support for multiple interoperating ZPlugSessions, that kind of multiplexing is done at a different level now, (z_plug_update_eof_mask): call user specified "finished" callback if the session ends, (z_plug_read_input): changed packet_stats invocation to pass self to it, (z_plug_session_start): set self->started, (z_plug_session_cancel): new function, cancels all pending callbacks, (z_plug_sessions_purge): removed, * lib/proxy.c (z_proxy_check_secondary): removed, (z_proxy_set_group, z_proxy_get_group): new function, gets and sets the proxy's group, (z_session_var_new, z_proxy_set_session_dict): removed, (z_proxy_run): renamed from z_proxy_run_method, as this is not going to be a virtual function, (z_proxy_threaded_start): renamed from z_proxy_start, added proxy_group argument, (z_proxy_nonblocking_start, z_proxy_nonblocking_stop): new functions, for alternative, nonblocking operation, (z_proxy_free_method): unref self->group added, * lib/zorp/proxy.h (ZPS_*): converted macros to enums, (ZS_MATCH_*): removed, (ZPF_NONBLOCKING): new enum, (ZProxyFuncs): added nonblocking_init and nonblocking_deinit, removed run, (ZProxy): added flags, group, fastpath * lib/proxystack.c (z_proxy_stack_remote_handshake_one): removed error argument, it is not a connector callback anymore, (z_proxy_stack_remote_handshake): adapted to the new connector blocking connect semantics * lib/proxygroup.c: new file, contains ZProxyGroup implementation * lib/zorp/proxygroup.h: new file, ZProxyGroup interface * lib/satyr.c (z_satyr_connect): don't use ZAttach as that requires a ZProxy pointer, use the blocking ZConnector API instead, * lib/zasauth.c (z_zas_connect): -"-, * lib/pyproxygroup.c, lib/zorp/pyproxygroup.h: new file, contains Python wrapper for ZProxyGroup * lib/pyattach.c (ZPolicyAttach): renamed from ZorpAttach, all z_py_zorp prefixes changed to z_policy_, only support blocking operation from Python, removed callback support, removed cancel method, followed API changes in attach.c * lib/pypolicy.c (z_policy_boot): removed obsolete fastpath references, follow renames in pyattach, added pyproxygroup module init * lib/pyproxy.c (ZPolicyProxy): structure made private, (z_policy_proxy_bind_implementation): new function, second stage of proxy initialization, the constructor will only create the "shell" of a proxy, bind_implementation is what actually connects the C implementation to the Python wrapper, (z_policy_proxy_getattr): added proxy_started attribute support, this way this assignment can be removed from Python, the Python wrapper will automatically "publish" a true value once the C part has been initialized, also removed support for session dictionaries, (z_policy_proxy_setattr): removed support for session dicts, (z_policy_proxy_init_instance): first part of initialization, only stores the necessary fields to initialize the proxy later, * lib/zorp/pyproxy.h (ZPolicyProxy): made private, (z_policy_proxy_check): added proper type checking so this one returns true for descendant Proxy instances as well * lib/zorp/pystruct.h: added Z_PST_PROXY_GROUP, * modules/ftp/ftp.c (ftp_data_prepare): follow ZAttach API changes, * modules/http/httpftp.c (http_ftp_initiate_passive_data): -"-, (http_ftp_complete_data): use http_ftp_cleanup_data instead of open coding * modules/plug/plug.c (PlugProxy): removed secondary connection support, as secondary connections are automatically handled by the core, follow ZPlugSession changes, implement nonblocking proxy interface * modules/pssl/pssl.c: follow API changes in plugsession * modules/rsh/rsh.c: follow attach changes * modules/tftp/tftp.c: follow attach changes * pylib/Zorp/Chainer.py: removed Attacher module import and setupFastpath methods * pylib/Zorp/Dispatcher.py (Dispatcher.connected): don't return the proxy instance * pylib/Zorp/Attach.py: removed * pylib/Zorp/NAT.py: removed setupFastpath methods, * pylib/Zorp/Router.py: removed setupFastpath methods, * pylib/Zorp/Proxy.py: removed setupFastpath invocations, (Proxy.stackProxy): start child proxies in a separate proxy group, * pylib/Zorp/Service.py (Service.__init__): added max_sessions attribute, create service specific proxy group, (Service.startInstance): start proxy in a ProxyGroup * pylib/Zorp/Session.py (MasterSession): removed client_tos and server_tos attributes, * pylib/Zorp/Router.py (AbstractRouter): removed ToS propagation * pylib/Zorp/Dispatch.py (Dispatcher): removed ToS query of the client connection * lib/pycore.c: removed ToS related functions * lib/satyr.c (z_satyr_connect): removed tos argument passed to z_stream_connector_new(), * lib/zasauth.c (z_zas_connect): -"- 2006-10-11 Krisztian Kovacs * pylib/Zorp/Service.py (PFService.buildKZorpMessage): dest_addr attribute of DirectedRouter is a SockAddr, the IP address is stored in network byte order so that we have to convert it * pylib/Zorp/Lib/nfnetlink.py: reformat source code (NfnetlinkMessage.get_attributes): fix attribute parsing, attribute lengths should be nfa_align()-ed when computing the position of the next attribute * pylib/Zorp/Lib/kznfnetlink.py: reformat source code, remove obsolete tests * pylib/Zorp/Lib/kznfnetlink.py: it's not meaningful to convert single bytes to host byte order * pylib/Zorp/NAT.py (GeneralNAT.getKZorpMapping): specify correct KZorp NAT range flags, return result list * pylib/Zorp/Service.py: import NAT_SNAT and NAT_DNAT from NAT (PFService.__init__): look up NAT policy names and store the policy reference (PFService.buildKZorpMessage): fix DirectedRouter destination address resolution, send NAT entry messages * pylib/Zorp/Dispatch.py (parsePortString): new function to parse port and port range lists (fixes: #10089) (AbstractDispatch.__init__): use parsePortString to initialize the rule_port attribute (fixes: #10089) (Dispatcher.buildKZorpMessage): self.rule_port is a list (fixes: #10089) * pylib/Zorp/Lib/kznfnetlink.py ({create,parse}_bind_addr_attr): new functions to construct and parse the new KZA_DPT_BIND_ADDR nfnetlink attributes (fixes: #10089) ({create,parse}_bind_{iface,ifgroup}_attr): handle port range lists (fixes: #10089) (create_add_dispatcher_{sabind,ifacebind,ifgroupbind}_msg): handle port range lists (fixes: #10089) * pylib/Zorp/Dispatch.py (AbstractDispatch): don't raise an error if rule_port argument is present for non-transparent dispatchers (fixes: #9945) * lib/pydispatch.c (z_policy_dispatch_bind_new): interface and interface group bind port arguments should not be converted to network byte order (fixes: #9944) * pylib/Zorp/Lib/kznfnetlink.py: remove endianness conversion from all create_ functions, from now on _all_ arguments are passed to these in host byte order (fixes: #9944) * pylib/Zorp/Dispatch.py (Dispatcher): don't convert anything to network byte order, as kznfnetlink functions require arguments in host byte order. There's one exception: ip attribute of SockAddr objects has to be converted to host byte order (fixes: #9944) * pylib/Zorp/Zone.py (InetZone): import socket module, convert address and mask to host byte order before passing it to the appropriate kznfnetlink function (fixes: #9944) * lib/dispatch.c (z_dispatch_bind_format): include port numbers in the format string, this is necessary to make these names unique (fixes: #9924) 2006-10-11 Balazs Scheidler * debian/zorp-pro.files.in: added KZorp specific files * pylib/Zorp/Zorp.py (init): added exception handling around KZorp configuration download 2006-10-11 Krisztian Kovacs * pylib/Zorp/Dispatch.py (Dispatcher.buildKZorpMessage): handle DBIfaceGroup dispatch bind type * pylib/Zorp/Lib/kznfnetlink.py (create_add_dispatcher_ifacebind_msg): fix create_name_attr() method name * pylib/Zorp/Zorp.py (init): remove hashmark left before the downloadKZorpConfig() call * pylib/Zorp/Dispatch.py (Dispatcher): interface listener's rule port is host byte order 2006-10-11 Balazs Scheidler * lib/zorp/dispatch.h (ZDispatchBind): avoid the use of unnammed structs as they'd clash as soon as interface group and interface bind lives in the same structure * lib/dispatch.c: follow ZDispatchBind member name changes in the header (ZDispatchChain): iface_watches member became a list, new member named iface_group_watch, (z_dispatch_bind_equal): added support for ZD_BIND_IFACE_GROUP, (z_dispatch_bind_hash): -"-, (z_dispatch_bind_format): -"-, (z_dispatch_bind_is_wildcard): -"-, (z_dispatch_iface_addr_matches): -"-, (z_dispatch_bind_new_iface_group): new function, creates an interface-group bind, (z_dispatch_bind_iface_change): changed to handle chain->registered_key being ZD_BIND_IFACE_GROUP type, earlier this was only used for ZD_BIND_IFACE binds, (z_dispatch_bind_iface_group_change): new function, registered as an interface group monitor, (z_dispatch_bind_listener): iface_watch became a list, follow the change, added support for ZD_BIND_IFACE_GROUP binds, (z_dispatch_unbind_listener): free ifgroup watch and the ifmon_watches list, * lib/ifmonitor.c (ZIfaceInfo): added group member, (ZIfmonGroupWatch): new struct, (z_ifmon_call_watchers_unlocked): removed unnecessary unlock call, (z_ifmon_watch_iface_matches): new function, returns if a given IfmonWatch refers to the interface given, (z_ifmon_register_watch): instead of calling z_ifmon_iterate_addrs that'd call all registered callbacks, use only the callback currently registered, might have caused some unnecessary binds, (z_ifmon_unregister_watch): call the callback for all known addresses before unregistering the watch, (z_ifmon_call_group_watchers_unlocked, z_ifmon_call_group_watchers): new functions, they iterate the group_watchers list, (z_ifmon_iterate_ifaces): new function call the callback for all registered interfaces in a group, (z_ifmon_register_group_watch, z_ifmon_unregister_group_watch): register/deregister an interface group watch, * lib/pydispatch.c (z_policy_dispatch_bind_new): added support for ZD_BIND_IFACE_GROUP, (z_policy_dispatch_bind_new_instance_iface_group): new function, constructs a DBIfaceGroup, * lib/pystruct.c (z_policy_struct_module_init): added Z_PST_DB_IFACE_GROUP * pylib/Zorp/Chainer.py (ConnectChainer.__init__): added timeout parameter documentation, renamed timeout to timeout_connect as it clashed in descendant classes, (ConnectChainer.establishConnection): use setServerAddress instead of setServer, (ConnectChainer.getNextTarget): new function, should return the next target to connect to, (ConnectChainer.connectTarget): new function, performs NAT mapping and establishes connection to the target server, (ConnectChainer.chainParent): remove server_address is a list hacks, use the new methods, simplified a lot, (MultiTargetChainer): new class, simple stateless, round-robin-like operation, (StateBasedChainer): new base class, implements state keeping and related methods, (FailoverChainer): stateful, failover HA, (RoundRobinChainer): new class, stateful round-robin * pylib/Zorp/Dispatch.py (Dispatcher.accepted): use setClientAddress instead of setClient * pylib/Zorp/Core.py: added imports for new classes * pylib/Zorp/Resolver.py (DNSResolver, HashResolver): make sure an array is returned * pylib/Zorp/Router.py: adapted to server/target address separation 2006-10-10 Balazs Scheidler * pylib/Zorp/NAT.py (NATPolicy.getKZorpMapping): new method, returns the KZorp representation of the given NAT policy, (GeneralNAT.getKZorpMapping): new method, returns the KZorp representation of GeneralNAT * pylib/Zorp/NAT.py (AbstractNAT.performTranslation): changed prototype to accept a tuple of addresses instead of a single address, (NATPolicy.performTranslation): support src/dst address tuple, (GeneralNAT): support source/destination match, (StaticNAT, OneToOneNAT, OneToOneMultiNAT, RandomNAT): follow interface change * pylib/Zorp/Domain.py (InetDomain, Inet6Domain): if address is not specified cover the entire address space (0.0.0.0/0 for ipv4, 0::0/0 for ipv6) 2006-10-10 Krisztian Kovacs * pylib/Zorp/Lib/kznfnetlink.py: renamed create_add_zonesvc_msg() to create_add_zone_svc_msg() to comply with the naming scheme of the module * pylib/Zorp/Zone.py (InetZone.buildKZorpMessage): iterate through inbound and outbound services and build ADD_ZONE_SVC messages * pylib/Zorp/Service.py: import DirectedRouter (PFService) temporarily disable NAT mapping generation, fix transparent flag reference * pylib/Zorp/Dispatch.py (Dispatcher.buildKZorpMessage): convert self.rule_port to network byte order before handing it to the kznfnetlink message builder * pylib/Zorp/Dispatch.py (AbstractDispatch): process transparent and rule_port attributes * pylib/Zorp/Dispatch.py (AbstractDispatch): store transparent and rule_port arguments in self * lib/pydispatch.c (struct _ZPolicyDispatch): remove transparent member; (z_policy_dispatch_getattr): no need to handle 'transparent' attribute as it's handled in Python now * pylib/Zorp/Zone.py (InetZone): pass inherit_name argument to Zone constructor; define an InetZone specific subZone() method so that InetZone subzones are also created as InetZone instances 2006-10-10 Balazs Scheidler * pylib/Zorp/Dispatch.py: remove debug print-outs * lib/pystruct.c: copy-paste the ZPolicyProxy type definition with some slight changes, now type() and isinstance() works properly on ZPolicyStruct objects 2006-10-10 Krisztian Kovacs * pylib/Zorp/Lib/kznfnetlink.py: adapt to latest kernel API changes (interface group dispatcher support) * pylib/Zorp/Dispatch.py: KZorp updates * pylib/Zorp/KZorp.py: fix imports (from X import * was not allowed here) * pylib/Zorp/Service.py: KZorp updates * pylib/Zorp/Zone.py: KZorp updates * pylib/Zorp/Zorp.py (init): don't catch AttributeError exceptions raised in the instance initializer function as this makes debugging impossible * lib/pypolicy.c (z_policy_init): run Python policy initializer function with CAP_NET_ADMIN enabled, this is required for Nfnetlink communication 2006-10-10 Balazs Scheidler * lib/pydispatch.c (z_policy_dispatch_bind_format): renamed from z_policy_dispatch_bind_pyformat, (z_policy_dispatch_bind_new): use different types for descendant DispatchBind types, * lib/pysockaddr.c (z_policy_sockaddr_new): use different types for descendant DispatchBind types, * lib/pystruct.c (z_policy_struct_module_init): added support for derived types, added info on new types * pylib/Zorp/Dispatch.py (convertSockAddrToDB): use isinstance instead of type() to determine compatibility * pylib/Zorp/Zorp.py: removed ugly storing of SockAddr type reference, it is now automatically done by the C part, 2006-10-10 Krisztian Kovacs * pylib/Zorp/KZorp.h: whitespace cleanup * pylib/Zorp/Makefile.am: subdirectory Lib added * pylib/Zorp/Lib/Makefile.am: added Makefile.am for new directory * configure.in.in: added pylib/Zorp/Lib/Makefile to the list of makefiles to be generated 2006-10-09 Krisztian Kovacs * pylib/Zorp/Lib/kznfnetlink.py: Update to match the latest specifications. * pylib/Zorp/Lib/nfnetlink.py: Import the (patched) socket module, remove test code. * pylib/Zorp/Service.py (PFService): Update to match the latest specifications, send flags and router target address to KZorp if necessary. 2006-10-09 MOLDVAI Dezso E. * pylib/Zorp/Router.py: Fixed DirectedRouter dest_addr gui type (fixes: #9604) * pylib/Zorp/Dispatch.py, pylib/Zorp/Zone.py: Fixed syntax errors (fixes: #nobug) 2006-10-09 Fazekas Andrea * pylib/Zorp/Dispatch.py: Removed the internal flag from the Dispatcher and CSZoneDispatcher classes. (fixes: #3683) 2006-08-29 SZALAY Attila * Initial log entry for version 3.2 zorp-3.9.5/autogen.sh000077500000000000000000000003771172670260400145370ustar00rootroot00000000000000#!/bin/sh # # $Id: autogen.sh,v 1.5 2004/08/18 11:25:39 bazsi Exp $ # # Run this script to generate Makefile skeletons and configure # scripts. # libtoolize -f --copy aclocal $* autoheader automake --add-missing --force-missing --copy --foreign autoconf zorp-3.9.5/configure.in000066400000000000000000000417631172670260400150530ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script. dnl dnl There are a couple of environment defined variables which this script dnl makes use of in addition to the standard CFLAGS/LDFLAGS/etc. These are: dnl dnl RELEASE_TAG - Debian release tag which is put to debian/changelog dnl SNAPSHOT_VERSION - snapshot version to add to version number dnl SOURCE_REVISION - Revision of the source-tree, will added to the version string dnl AC_INIT(zorp/main.c) dnl *************************************************************************** dnl Definitions PACKAGE=zorp-pro BROCHURE_VERSION="3.9" VERSION=`cat $srcdir/VERSION` ZORP_PRODUCT_NAME="Zorp Professional" ZORP_LICENSE_VERSION="3.4" dnl *************************************************************************** dnl Dependencies GLIB_MIN_VERSION="2.2.1" ZORPLL_MIN_VERSION="3.9.1.0" ZORPLIC_MIN_VERSION="3.9.1" ZORPMISC_MIN_VERSION="3.9.1" OPENSSL_MIN_VERSION="0.9.8" PYTHON_MIN_VERSION="2.6" dnl *************************************************************************** dnl Initial setup # We want an absolute path to the source-dir. case "$srcdir" in /*) top_srcdir=$srcdir ;; *) oldsrcdir="$srcdir" top_srcdir="`cd \"$srcdir\"; pwd`" ;; esac if test -r $srcdir/dist.conf; then # read defaults, dist.conf does not change # values for parameters that are already set source $srcdir/dist.conf fi if test -z "$ZORP_PACKAGE_LICENSE"; then ZORP_PACKAGE_LICENSE="pro" fi if test "$ZORP_PACKAGE_LICENSE" = "gpl"; then PACKAGE="zorp" PACKAGE_SUFFIX="" elif test "$ZORP_PACKAGE_LICENSE" = "pro"; then PACKAGE_SUFFIX="-pro" else AC_ERROR(Invalid license type: %s) fi AM_CONDITIONAL(PRO, test x$ZORP_PACKAGE_LICENSE = xpro) AM_INIT_AUTOMAKE($PACKAGE, $VERSION, no-define) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES]) if test -n "$SNAPSHOT_VERSION"; then VERSION=$VERSION+$SNAPSHOT_VERSION fi if test -z "$RELEASE_TAG"; then RELEASE_TAG=zorp30dbg fi AM_CONFIG_HEADER(lib/zorp/zorpconfig.h) AC_PREFIX_DEFAULT(/usr/local) if test "x$prefix" = "xNONE"; then prefix=$ac_default_prefix fi zorp_srcdir=$top_srcdir AC_ARG_ENABLE(debug, [ --enable-debug Enable debug information & messages (default: no)],, enable_debug=no) AC_ARG_ENABLE(trace, [ --enable-trace Enable function call tracing (default: no)],, enable_trace=no) AC_ARG_ENABLE(werror, [ --enable-werror When enabled, consider compiler warnings as errors (default: yes)],, enable_werror=yes) AC_ARG_ENABLE(coverage, [ --enable-coverage Enable runtime coverage information generation (default: no)],, enable_coverage=no) AC_ARG_ENABLE(ip_options, [ --enable-ip-options Enable handling IP options (EXPERIMENTAL)],, enable_ip_options=no) AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Enable IPv6 support (EXPERIMENTAL)],, enable_ipv6=yes) AC_ARG_ENABLE(prefork, [ --enable-prefork Enable prefork],, enable_prefork=no) AC_ARG_WITH(python-headers, [ --with-python-headers=[path] Python headers are located at path]) AC_ARG_WITH(python-libs, [ --with-python-libs=[path] Python libraries are located at path], LDFLAGS="$LDFLAGS -L$with_python_libs") AC_ARG_WITH(python, [ --with-python=[path] Python binary is located at path], PYTHON="$with_python") AC_ARG_WITH(pidfiledir, [ --with-pidfiledir=[path] Path to run directory where pidfile is stored], pidfiledir="$with_pidfiledir", pidfiledir='${prefix}/var/run/zorp/') sysconfdir="${sysconfdir}/zorp" #datadir='${prefix}/share/zorp' libdir='${prefix}/lib' dnl *************************************************************************** dnl Argument processing which might affect detection if test "x$enable_debug" = "xyes"; then # maybe we should check whether /usr/lib/debug exists LIBS="$LIBS -L/usr/lib/debug" CFLAGS="$CFLAGS -O0" else CFLAGS="$CFLAGS -O2 " fi dnl *************************************************************************** dnl Checks for programs. AC_PROG_CC AC_PROG_CC_C99 AC_PROG_CPP AC_DISABLE_STATIC AM_PROG_LIBTOOL AC_PATH_PROG(GPERF, gperf) if test "x$GPERF" = "x"; then AC_ERROR(No gperf found) fi if test "x$PYTHON" = "x"; then AC_PATH_PROG(PYTHON, python, none) fi if test "x$PYTHON" = "xnone"; then AC_ERROR(python interpreter required) fi AC_MSG_CHECKING(Python version and location) PYTHON_PREFIX=`$PYTHON -c "import sys; print sys.prefix"` PYTHON_VERSION_MAJOR=[`$PYTHON -c "import sys; print '%d' % (sys.version_info[0]);"`] PYTHON_VERSION_MINOR=[`$PYTHON -c "import sys; print '%d' % (sys.version_info[1]);"`] PYTHON_VERSION="${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}" PYTHON_DEBUG_POSTFIX=[`$PYTHON -c "import sys; print '_d' if sys.pydebug else '';"`] AC_MSG_RESULT([$PYTHON, $PYTHON_VERSION, $PYTHON_PREFIX]) AC_MSG_CHECKING(Whether Python is at least 2.3) if test $PYTHON_VERSION_MAJOR -lt 2 -o $PYTHON_VERSION_MAJOR -eq 2 -a $PYTHON_VERSION_MINOR -lt 3; then AC_MSG_RESULT(no) AC_ERROR(Zorp requires at least Python 2.3) fi AC_MSG_RESULT(yes) dnl *************************************************************************** dnl Checks for header files. AC_CHECK_HEADERS(sys/capability.h sys/prctl.h limits.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_CACHE_CHECK(for MSG_PROXY, blb_cv_msg_proxy, [AC_EGREP_CPP(MSG_PROXY, [ #include #include #ifdef MSG_PROXY MSG_PROXY #endif ], blb_cv_msg_proxy=yes, blb_cv_msg_proxy=no)]) if test "x$blb_cv_msg_proxy" = "xyes"; then AC_DEFINE(HAVE_MSG_PROXY, 1, [Have MSG_PROXY flag (Linux 2.2)]) fi if test "x$with_python_headers" = "x"; then PYTHON_CFLAGS="-I$PYTHON_PREFIX/include/python$PYTHON_VERSION$PYTHON_DEBUG_POSTFIX" else PYTHON_CFLAGS=$with_python_headers fi save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $PYTHON_CFLAGS" AC_CHECK_HEADERS(Python.h) if test "x$ac_cv_header_Python_h" = "xno"; then AC_ERROR(Python.h not found) fi CPPFLAGS=$save_CPPFLAGS dnl *************************************************************************** dnl Checks for libraries. dnl Checks for library functions. AC_CHECK_LIB(socket, setsockopt, SOCK_LIBS="$SOCK_LIBS -lsocket") AC_CHECK_LIB(xnet, inet_addr, SOCK_LIBS="$SOCK_LIBS -lxnet") AC_CHECK_LIB(nsl, gethostbyname, SOCK_LIBS="$SOCK_LIBS -lnsl") dnl Python might required -ldl, -lutil & -lm AC_CHECK_LIB(rt, aio_read, PYTHON_DEPS="$PYTHON_DEPS -lrt") AC_CHECK_LIB(m, sin, PYTHON_DEPS="$PYTHON_DEPS -lm") AC_CHECK_LIB(util, openpty, PYTHON_DEPS="$PYTHON_DEPS -lutil") AC_CHECK_LIB(dl, dlsym, PYTHON_DEPS="$PYTHON_DEPS -ldl") AC_CHECK_LIB(z, inflate, ZLIB_LIBS="$ZLIB_LIBS -lz") save_LIBS=$LIBS LIBS="$LIBS $SOCK_LIBS" AC_CHECK_FUNCS(socket) AC_CHECK_FUNCS(select snprintf vsnprintf strerror inet_aton) AC_CHECK_FUNCS(prctl gethostbyname_r) AC_CACHE_CHECK(for PR_SET_DUMPABLE, blb_cv_dumpable, [AC_EGREP_CPP(PR_SET_DUMPABLE, [ #include PR_SET_DUMPABLE ], blb_cv_dumpable=no, blb_cv_dumpable=yes)]) if test "x$blb_cv_dumpable" = "xyes"; then AC_DEFINE(HAVE_PR_SET_DUMPABLE, 1, [Zorp may enable core_dumping Linux 2.4-]) fi LIBS=$save_LIBS PKG_CHECK_MODULES(GLIB, glib-2.0 >= $GLIB_MIN_VERSION,, AC_MSG_ERROR(Cannot find GLib library version >= $GLIB_MIN_VERSION: is pkg-config in path?)) PKG_CHECK_MODULES(GMODULE, gmodule-2.0 >= $GLIB_MIN_VERSION,, AC_MSG_ERROR(Cannot find GModule library version >= $GLIB_MIN_VERSION: is pkg-config in path?)) PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= $GLIB_MIN_VERSION,, AC_MSG_ERROR(Cannot find GThread library version >= $GLIB_MIN_VERSION: is pkg-config in path?)) PKG_CHECK_MODULES(ZORPLL, zorplibll >= $ZORPLL_MIN_VERSION,, AC_MSG_ERROR(Cannot find ZORP Lowlevel library version >= $ZORPLL_MIN_VERSION: is pkg-config in path?)) PKG_CHECK_MODULES(OPENSSL, openssl >= $OPENSSL_MIN_VERSION,, AC_MSG_ERROR(Cannot find OpenSSL library version >= $OPENSSL_MIN_VERSION: is pkg-config in path?)) if test "$ZORP_PACKAGE_LICENSE" = "pro"; then save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $OPENSSL_CFLAGS" AC_CHECK_HEADER(openssl/ts.h, [], [AC_MSG_ERROR([OpenSSL header file is missing])]) CPPFLAGS=$save_CPPFLAGS PKG_CHECK_MODULES(ZORPLIC, libzlicense >= $ZORPLIC_MIN_VERSION,, AC_MSG_ERROR(Cannot find ZORP license library version >= $ZORPLIC_MIN_VERSION: is pkg-config in path?)) PKG_CHECK_MODULES(ZORPMISC, libzmisc >= $ZORPMISC_MIN_VERSION,, AC_MSG_ERROR(Cannot find Zorp Misc library version >= $ZORPMISC_MIN_VERSION: is pkg-config in path?)) AC_CHECK_LIB(pcap, pcap_open_live, PCAP_LIBS="$PCAP_LIBS -lpcap") AC_CHECK_HEADERS(google/coredumper.h) AC_CHECK_LIB(coredumper, WriteCoreDump, COREDUMP_LIBS="$COREDUMP_LIBS -lcoredumper") fi dnl dnl Locating Python libraries dnl if test "x$with_python_libs" != "xno"; then found=no for pylibpath in '' $PYTHON_PREFIX/lib $PYTHON_PREFIX/lib/python$PYTHON_VERSION/config; do eval `echo unset ac_cv_lib_python$PYTHON_VERSION'_'Py_Finalize | tr '.' '_'` eval `echo unset ac_cv_lib_python$PYTHON_VERSION''___Py_Finalize | tr '.' '_'` save_LIBS=$LIBS if test "x$pylibpath" != "x"; then PYTHON_LIBS="-L$pylibpath -lpython$PYTHON_VERSION $PYTHON_DEPS" else PYTHON_LIBS="-lpython$PYTHON_VERSION $PYTHON_DEPS" fi LIBS="$LIBS $PYTHON_LIBS" AC_CHECK_LIB(python$PYTHON_VERSION$PYTHON_DEBUG_POSTFIX, Py_Finalize, found=yes,,$PYTHON_DEPS) LIBS=$save_LIBS if test "x$found" = "xyes"; then break fi done if test "x$found" != "xyes"; then AC_ERROR(Python development libraries required) fi else PYTHON_LIBS="$with_python_libs $PYTHON_DEPS" fi if test "x$ac_cv_func_gethostbyname_r" = "xyes"; then dnl if test "`uname -s`" = "Linux"; then dnl AC_DEFINE(HAVE_LEAK_IN_GETHOSTBYNAME_R, 1, [We have a leak in gethostbyname_r, avoid using it]) if test "`uname -s`" = "SunOS"; then AC_DEFINE(HAVE_SUN_GETHOSTBYNAME_R, 1, [We have a Solaris style gethostbyname_r];) fi fi save_LIBS="$LIBS" LIBS="$LIBS $OPENSSL_LIBS" AC_SEARCH_LIBS(DES_ecb_encrypt, eay32 crypto, AC_SEARCH_LIBS(des_ecb_encrypt, eay32 crypto)) AC_SEARCH_LIBS(SSL_accept, ssl32 ssl) AC_CHECK_FUNC(ENGINE_by_id) LIBS=$save_LIBS if test "x$ac_cv_search_SSL_accept" = "xno"; then AC_ERROR(OpenSSL libraries required) fi dnl *************************************************************************** dnl Misc checks AC_SYS_LARGEFILE ZORP_PROC="`uname -m`" ZORP_OS="`uname -s`" ZORP_OS_RELEASE="`uname -r | cut -d '.' -f 1-2`" if test "x$ZORP_OS" = "xLinux";then CURRDATE=`date -R` else CURRDATE=`date +"%a, %d %b %Y %H:%M:%S %Z"` fi dnl *************************************************************************** dnl Export detected settings to Makefiles and Zorp AC_SUBST(zorp_srcdir) AC_SUBST(ZORP_PROC) AC_SUBST(ZORP_OS) zorpeval() { OLD=$1 NEW=`eval echo $1` while /usr/bin/test "x$OLD" != "x$NEW" do OLD=$NEW NEW=`eval echo $OLD` done echo $OLD } AC_DEFINE_UNQUOTED(ZORP_SYSCONFDIR, "`zorpeval $sysconfdir`", [sysconfdir]) AC_DEFINE_UNQUOTED(ZORP_DATADIR, "`zorpeval $datadir/zorp`", [datadir]) AC_DEFINE_UNQUOTED(ZORP_LIBDIR, "`zorpeval $libdir/zorp`", [libdir]) AC_DEFINE_UNQUOTED(ZORP_STATEDIR, "`zorpeval $localstatedir`", [localstatedir]) AC_DEFINE_UNQUOTED(ZORP_PIDFILEDIR, "`zorpeval $pidfiledir`", [pidfiledir]) CFLAGS="$CFLAGS -Wall -W -Werror-implicit-function-declaration -Wno-pointer-sign -g -D_GNU_SOURCE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" dnl *************************************************************************** dnl Export results enable_value() { if test "x$1" = "xyes" ; then echo 1 else echo 0 fi } dnl Library versioning, the resulting shared object is named: dnl zorplib-.so. dnl VERSION_NOSNAPSHOT="`echo $VERSION | cut -d '+' -f 1`" VERSION_MAJOR=`echo $VERSION_NOSNAPSHOT | cut -d '.' -f 1` VERSION_MINOR=`echo $VERSION_NOSNAPSHOT | cut -d '.' -f 2` VERSION_COMPAT=`echo $VERSION_NOSNAPSHOT | cut -d '.' -f 3` VERSION_AGE=0 LIBZORP_LT_RELEASE="$VERSION_MAJOR.$VERSION_MINOR" LIBZORP_LT_CURRENT=$@<:@ $VERSION_COMPAT + $VERSION_AGE @:>@ LIBZORP_LT_COMPAT_BRANCH="$VERSION_MAJOR.$VERSION_MINOR-$VERSION_COMPAT" LIBZORP_LT_COMPAT_PL="$VERSION_COMPAT" LIBZORP_LT_AGE="$VERSION_AGE" dnl *************************************************************************** dnl Size check of pointers AC_CHECK_SIZEOF(void *) AC_SUBST(RELEASE_TAG) AC_SUBST(BROCHURE_VERSION) AC_SUBST(SNAPSHOT_VERSION) AC_SUBST(SOURCE_REVISION) AC_SUBST(ZORP_PACKAGE_LICENSE) AC_SUBST(PACKAGE_SUFFIX) AC_DEFINE_UNQUOTED(ZORP_LICENSE_VERSION, "$ZORP_LICENSE_VERSION", [Required license version]) AC_DEFINE_UNQUOTED(ZORP_PRODUCT_NAME, "$ZORP_PRODUCT_NAME", [Required product name in license]) AC_DEFINE_UNQUOTED(BROCHURE_VERSION, "$BROCHURE_VERSION", [Zorp brochure version]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Zorp package version]) AC_DEFINE_UNQUOTED(ZORP_SOURCE_REVISION, "$SOURCE_REVISION", [Zorp source revision number]) AC_DEFINE_UNQUOTED(ENABLE_DEBUG, `enable_value $enable_debug`, [Enable debugging]) AC_DEFINE_UNQUOTED(ENABLE_TRACE, `enable_value $enable_trace`, [Enable trace messages]) AC_DEFINE_UNQUOTED(ENABLE_IPOPTIONS, `enable_value $enable_ip_options`, [Enable IP option processing]) AC_DEFINE_UNQUOTED(ENABLE_IPV6, `enable_value $enable_ipv6`, [Enable IPv6 support]) AC_DEFINE_UNQUOTED(ENABLE_PREFORK, `enable_value $enable_prefork`, [Enable prefork support]) # CPPFLAGS as supplied by dependencies DEPS_CPPFLAGS="$CPPFLAGS $ZORPLL_CFLAGS $ZORPLIC_CFLAGS $ZORPMISC_CFLAGS $PYTHON_CFLAGS $OPENSSL_CFLAGS" # CPPFLAGS defined to access local header files in addition to dependencies CPPFLAGS="-I$top_srcdir/lib -I$top_srcdir $DEPS_CPPFLAGS" # common libraries (dependencies) LIBPROXY_CPPFLAGS="-I$top_srcdir/libproxy/ $CPPFLAGS" MODULES_CPPFLAGS="$LIBPROXY_CPPFLAGS" # Dependencies outside the source tree DEPS_LIBS="$SOCK_LIBS $ZORPLL_LIBS $ZORPLIC_LIBS $ZORPMISC_LIBS $PYTHON_LIBS $COREDUMP_LIBS $OPENSSL_LIBS" # zorp library libs LIBZORP_LIBS="$DEPS_LIBS $GLIB_LIBS $GMODULE_LIBS $GTHREAD_LIBS $PYTHON_LIBS $OPENSSL_LIBS $PCAP_LIBS" # Zorp main program ZORP_LIBS="-L\$(top_builddir)/lib/ -lzorp $ZORPLL_LIBS" # libraries to link the zorp modules against (implicitly linked # against the main binary, those does not need to be listed here MODULES_LIBS="-L\$(top_builddir)/lib/ -lzorp -L\$(top_builddir)/libproxy/ -lzorpproxy $ZORPLL_LIBS $PYTHON_LIBS" # module test program libs MODULETESTS_LIBS="$MODULES_LIBS $ZORP_LIBS" dnl *************************************************************************** dnl Coverage flags and libs if test "x$enable_coverage" = "xyes"; then LDFLAGS="$LDFLAGS -fprofile-arcs -lgcov" CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" fi if test "x$enable_werror" = "xyes"; then CFLAGS="$CFLAGS -Werror" fi AC_SUBST(DEPS_CPPFLAGS) AC_SUBST(DEPS_LIBS) AC_SUBST(ZLIB_LIBS) AC_SUBST(GLIB_LIBS) AC_SUBST(OPENSSL_LIBS) AC_SUBST(OPENSSL_CLAGS) AC_SUBST(PCAP_LIBS) AC_SUBST(PYTHON_LIBS) AC_SUBST(PYTHON_CFLAGS) AC_SUBST(LIBZORP_LIBS) AC_SUBST(ZORP_LIBS) AC_SUBST(LIBPROXY_CPPFLAGS) AC_SUBST(MODULES_LIBS) AC_SUBST(MODULES_CPPFLAGS) AC_SUBST(MODULETESTS_LIBS) dnl for zorpctl AC_SUBST(SOCK_LIBS) date=`date +%Y/%m/%d` AC_DEFINE_UNQUOTED(ZORP_CONFIG_DATE, "$date", [Configuration date]) TESTCASES="" dnl for mod in $MODULE_DIRS; do dnl TESTCASES="${TESTCASES} `cd $top_srcdir/tests; find functional/$mod -type f -a ! -path '*.arch-ids*' | tr '\n' ' '`" dnl done AC_SUBST(TESTCASES) cat < .\" Date: 03/06/2012 .\" Manual: [FIXME: manual] .\" Source: [FIXME: source] .\" Language: English .\" .TH "INSTANCES\&.CONF" "5" "03/06/2012" "[FIXME: source]" "[FIXME: manual]" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" instances.conf_ \- zorp(8) instances database .SH "DESCRIPTION" .PP The instances\&.conf file describes the \fBzorp(8)\fR instances to be run on the system\&. It is processed by \fBzorpctl(8)\fR line by line, each line having the structure described below\&. Empty lines and lines beginning with \*(Aq#\*(Aq are comments ignored by \fBzorpctl\fR\&. .SH "STRUCTURE" .PP \fBinstance\-name parameters [\-\- zorpctl\-options]\fR .PP \fIinstance\-name\fR is the name of the Zorp instance to be started; it is passed to \fBzorp\fR with its \fB\-\-as\fR parameter\&. Instance names may consist of the characters [a\-zA\-Z0\-9_] and must begin with a letter\&. .PP \fIparameters\fR are space separated parameters entered into the zorp command\-line\&. For details on these command\-line parameters see zorp(8)\&. .PP \fIzorpctl\-options\fR are space separated parameters control startup specific options\&. They are processed by \fBzorpctl\fR itself\&. The following \fBzorpctl\fR options are available: .PP \fB\-\-auto\-restart\fR or \fB\-A\fR .RS 4 Enable the automatic restart feature of \fBzorpctl\fR\&. When an instance is in auto\-restart mode, it is restarted automatically in case the instance exits\&. .RE .PP \fB\-\-no\-auto\-restart\fR or \fB\-a\fR .RS 4 Disable automatic restart for this instance\&. .RE .PP \fB\-\-fd\-limit \fR or \fB\-f \fR .RS 4 Set the file descriptor limit to \&. The file descriptor limit defaults to the number of threads (specified by the \fI\-\-threads\fR parameter of zorp(8)) multiplied by 4\&. .RE .PP \fB\-\-process\-limit \fR or \fB\-p \fR .RS 4 Set the process limit to \&. The process limit defaults to the number of threads (specified by the \fI\-\-threads\fR parameter of zorp(8)) multiplied by 2\&. .RE .PP \fB\-\-enable\-core\fR .RS 4 Explicitly enable core dumps for Zorp processes\&. The core limit is inherited from the local starting environment (e\&.g\&.: starting shell) if not specified\&. .RE .PP \fB\-\-parallel\-instances \fR or \fB\-P \fR .RS 4 Run of processes for the instance\&. \fBzorpctl\fR starts exactly one Zorp process in master mode and of slave Zorp processes\&. This mode of operation is incompatible with old\-style dispatchers, you must use the new rule\-based policy with this option\&. .RE .SH "EXAMPLES" .PP .PP \fBzorp_ftp \-\-policy /etc/zorp/policy\&.py \-\-verbose 5 \fR .PP The line above describes a Zorp instance named \fIzorp_ftp\fR using policy file \fI/etc/zorp/policy\&.py\fR, and having verbosity level 5\&. .PP \fBzorp_intra \-v4 \-p /etc/zorp/policy\&.py \-\-threads 500 \-\-no\-auto\-restart \-\-fd\-limit 1024 \-\-process\-limit 512\fR .PP This line describes a zorp instance named \fIzorp_intra\fR using the policy file /etc/zorp/policy\&.py, verbosity level 4\&. The maximum number of threads is set to 500, file descriptor limit to 1024, process limit to 512\&. .SH "FILES" .PP The default location of instances\&.conf is /etc/zorp/instances\&.conf\&. Defaults for zorpctl tunables can be specified in /etc/zorp/zorpctl\&. .SH "AUTHOR" .PP This manual page was written by the BalaBit Documentation Team \&. .SH "COPYRIGHT" .PP Copyright \(co 2006 BalaBit IT Security Ltd\&. All rights reserved\&. For more information about the legal status of this document please read: \m[blue]\fBhttp://www\&.balabit\&.com/products/zorp/docs/legal_notice\&.bbq\fR\m[] zorp-3.9.5/doc/man/policy.py.5000066400000000000000000000060211172670260400160620ustar00rootroot00000000000000'\" t .\" Title: policy.py .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.75.2 .\" Date: 03/06/2012 .\" Manual: [FIXME: manual] .\" Source: [FIXME: source] .\" Language: English .\" .TH "POLICY\&.PY" "5" "03/06/2012" "[FIXME: source]" "[FIXME: manual]" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" policy.py_ \- zorp(8) policy file\&. .SH "DESCRIPTION" .PP The policy\&.py file is a Python module containing the zone and service definitions and other policy related settings used by \fBzorp(8)\fR\&. Empty lines and lines beginning with \*(Aq#\*(Aq are comments and are ignored\&. .PP The policy\&.py file is generated automatically by ZMC, the Zorp Management Console, or it can be edited manually\&. .PP IMPORTANT: Do not edit manually a file generated by ZMC, because the manual changes will not be retained by ZMC and will be lost when re\-generating the file\&. .SH "FILES" .PP The default location of policy\&.py is /etc/zorp/policy\&.py\&. .SH "SEE ALSO" .PP For further information on policy\&.py refer to the following sources: .PP A tutorial on manually editing the policy\&.py file can be found at \m[blue]\fBhttp://www\&.balabit\&.com/network\-security/zorp\-gateway/gpl/tutorial/\fR\m[]\&\s-2\u[1]\d\s+2\&. .PP Additional information can also be found in the \fIZorp Administrator\*(Aqs Guide\fR, the \fIZorp Reference Guide\fR, and in the various tutorials available at the BalaBit Documentation Page at \m[blue]\fBhttp://www\&.balabit\&.com/support/documentation/\fR\m[]\&\s-2\u[2]\d\s+2\&. .SH "AUTHOR" .PP This manual page was written by the BalaBit Documentation Team \&. .SH "COPYRIGHT" .PP Copyright \(co 2006 BalaBit IT Security Ltd\&. All rights reserved\&. For more information about the legal status of this document please read: \m[blue]\fBhttp://www\&.balabit\&.com/products/zorp/docs/legal_notice\&.bbq\fR\m[] .SH "NOTES" .IP " 1." 4 http://www.balabit.com/network-security/zorp-gateway/gpl/tutorial/ .RS 4 \%http://www.balabit.com/network-security/zorp-gateway/gpl/tutorial/ .RE .IP " 2." 4 http://www.balabit.com/support/documentation/ .RS 4 \%http://www.balabit.com/support/documentation/ .RE zorp-3.9.5/doc/man/zorp.8000066400000000000000000000137301172670260400151360ustar00rootroot00000000000000'\" t .\" Title: zorp .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.75.2 .\" Date: 03/06/2012 .\" Manual: [FIXME: manual] .\" Source: [FIXME: source] .\" Language: English .\" .TH "ZORP" "8" "03/06/2012" "[FIXME: source]" "[FIXME: manual]" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" zorp_ \- Zorp Firewall Suite .SH "SYNOPSIS" .HP \w'\fBzorp\fR\ 'u \fBzorp\fR [options] .SH "DESCRIPTION" .PP The \fBzorp\fR command is the main entry point for a Zorp instance, and as such it is generally called by zorpctl(8) with command line parameters specified in instances\&.conf(5)\&. .SH "OPTIONS" .PP \fB\-\-version\fR or \fB\-V\fR .RS 4 Display version number and compilation information\&. .RE .PP \fB\-\-as \fR or \fB\-a \fR .RS 4 Set instance name to \&. Instance names may consist of the characters [a\-zA\-Z0\-9_] and must begin with a letter\&. Log messages of this instance are prefixed with this name\&. .RE .PP \fB\-\-also\-as \fR or \fB\-A \fR .RS 4 Add a secondary instance named \&. Secondary instances share the same Zorp process but they have a separate section in the configuration file\&. .RE .PP \fB\-\-policy \fR or \fB\-p \fR .RS 4 Use the file called as policy\&. This file must be a valid policy file\&. .RE .PP \fB\-\-verbose \fR or \fB\-v \fR .RS 4 Set verbosity level to , or if is omitted increment it by one\&. Default the verbosity level is 3; possible values are 0\-10\&. .RE .PP \fB\-\-pidfile \fR or \fB\-P \fR .RS 4 Set path to the PID file where the pid of the main process is stored\&. .RE .PP \fB\-\-foreground\fR or \fB\-F\fR .RS 4 Do not daemonize, run in the foreground\&. .RE .PP \fB\-\-process\-mode \fR .RS 4 Set processing mode to one of background, safe\-background or foreground\&. .RE .PP \fB\-\-no\-syslog\fR or \fB\-l\fR .RS 4 Send log messages to the standard output instead of syslog\&. .RE .PP \fB\-\-log\-tags\fR or \fB\-T\fR .RS 4 Prepend log category and log level to each message\&. .RE .PP \fB\-\-log\-escape\fR .RS 4 Escape non\-printable characters to avoid binary log files\&. Each character less than 0x20 and greater than 0x7F are escaped in the form \&. .RE .PP \fB\-\-log\-spec \fR or \fB\-s \fR .RS 4 Set verbosity mask on a per category basis\&. Each log message has an assigned multi\-level category, where levels are separated by a dot\&. For example, HTTP requests are logged under \fIhttp\&.request\fR\&. \fB\fR is a comma separated list of log specifications\&. A single log specification consists of a wildcard matching log category, a colon, and a number specifying the verbosity level of that given category\&. Categories match from left to right\&. E\&.g\&.: \fB\-\-logspec \*(Aqhttp\&.*:5,core:3\*(Aq\fR\&. The last matching entry will be used as the verbosity of the given category\&. If no match is found the default verbosity specified with \fB\-\-verbose\fR is used\&. .RE .PP \fB\-\-threads \fR or \fB\-t \fR .RS 4 Set the maximum number of threads that can be used in parallel by this Zorp instance\&. .RE .PP \fB\-\-idle\-threads \fR or \fB\-I\fR .RS 4 Set the maximum number of idle threads; this option has effect only if threadpools are enabled (see the option \fB\-\-threadpools\fR)\&. .RE .PP \fB\-\-threadpools\fR or \fB\-O\fR .RS 4 Enable the use of threadpools, which means that threads associated with sessions are not automatically freed, only if the maximum number of idle threads is exceeded\&. .RE .PP \fB\-\-user \fR or \fB\-u \fR .RS 4 Switch to the supplied user after starting up\&. .RE .PP \fB\-\-group \fR or \fB\-g \fR .RS 4 Switch to the supplied group after starting up\&. .RE .PP \fB\-\-chroot \fR or \fB\-R \fR .RS 4 Change root to the specified directory before reading the configuration file\&. The directory must be set up accordingly\&. .RE .PP \fB\-\-caps \fR or \fB\-C \fR .RS 4 Switch to the supplied set of capabilities after starting up\&. This should contain the required capabilities in the permitted set\&. For the syntax of capability description see the man page cap_from_text(3)\&. .RE .PP \fB\-\-no\-caps\fR or \fB\-N\fR .RS 4 Do not change capabilities at all\&. .RE .PP \fB\-\-crypto\-engine \fR or \fB\-E \fR .RS 4 Set the OpenSSL crypto engine to be used for hardware accelerated crypto support\&. .RE .PP \fB\-\-stack\-size \fR or \fB\-S \fR .RS 4 Set the maximum stack size used by threads\&. Note that the maximum number of parallel threads is influenced by the size specified here\&. The default stack size is 512 KB, the maximum you can set is 8192 KB\&. .RE .SH "FILES" .PP /etc/zorp/ .PP /etc/zorp/policy\&.py .PP /etc/zorp/instances\&.conf .SH "AUTHOR" .PP This manual page was written by the BalaBit Documentation Team \&. .SH "COPYRIGHT" .PP Copyright \(co 2006 BalaBit IT Security Ltd\&. All rights reserved\&. For more information about the legal status of this document please read: \m[blue]\fBhttp://www\&.balabit\&.com/products/zorp/docs/legal_notice\&.bbq\fR\m[] zorp-3.9.5/doc/man/zorpctl.8000066400000000000000000000121521172670260400156360ustar00rootroot00000000000000'\" t .\" Title: zorpctl .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.75.2 .\" Date: 03/06/2012 .\" Manual: [FIXME: manual] .\" Source: [FIXME: source] .\" Language: English .\" .TH "ZORPCTL" "8" "03/06/2012" "[FIXME: source]" "[FIXME: manual]" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" zorpctl_ \- Start and stop zorp instances\&. .SH "SYNOPSIS" .HP \w'\fBzorpctl\ command\fR\ 'u \fBzorpctl command\fR [options\ [instances/@instance\-list\-file]] .SH "DESCRIPTION" .PP \fBzorpctl\fR starts and stops zorp(8) instances based on the contents of the instances\&.conf(5) file\&. Multiple instance names can be specified in the command\-line or in a file to start or stop several instances\&. If an error occurs while stopping or starting an instance, an exclamation mark is appended to the instance name as \fBzorpctl\fR processes the request, and a summary is printed when the program exits\&. If no instance is specified, the command is executed on all instances\&. The instances to be controlled can be specified in a file instead of listing them in the command line, e\&.g\&.: \fBzorpctl command options instances\&.txt\fR\&. The instances\&.txt should contain every instance name in a new line\&. .SH "COMMANDS" .PP \fBstart\fR .RS 4 Starts the specified Zorp instance(s)\&. .RE .PP \fBforce\-start\fR .RS 4 Starts the specified Zorp instance(s) even if they are disabled\&. .RE .PP \fBstop\fR .RS 4 Stops the specified Zorp instance(s)\&. .RE .PP \fBforce\-stop\fR .RS 4 Forces the specified Zorp instance(s) to stop using the KILL signal\&. .RE .PP \fBrestart\fR .RS 4 Restart the specified Zorp instance(s)\&. .RE .PP \fBforce\-restart\fR .RS 4 Forces the specified Zorp instance(s) to restart by stopping them using the KILL signal\&. .RE .PP \fBreload\fR .RS 4 Reload the specified Zorp instance(s)\&. .RE .PP \fBstatus\fR .RS 4 Display the status of the specified Zorp instance(s)\&. .PP \fB\-\-verbose\fR or \fB\-v\fR .RS 4 Display detailed status information\&. .RE .RE .PP \fBgui\-status\fR .RS 4 Display the status of the specified Zorp instance(s) in an internal format easily parsable by ZMC\&. NOTE: This command is mainly used internally within Zorp, and the structure of its output may change\&. .RE .PP \fBversion\fR .RS 4 Display version information on Zorp\&. .RE .PP \fBinclog\fR .RS 4 Raise the verbosity (log) level of the specified Zorp instance(s) by one\&. .RE .PP \fBdeclog\fR .RS 4 Decrease the verbosity (log) level of the specified Zorp instance(s) by one\&. .RE .PP \fBlog\fR .RS 4 Change various log related settings in the specified Zorp instance(s) using the following options: .PP \fB\-\-vinc\fR or \fB\-i\fR .RS 4 Increase verbosity level by one\&. .RE .PP \fB\-\-vdec\fR or \fB\-d\fR .RS 4 Decrease verbosity level by one\&. .RE .PP \fB\-\-vset \fR or \fB\-s \fR .RS 4 Set verbosity level to \&. .RE .PP \fB\-\-log\-spec \fR or \fB\-S \fR .RS 4 Set verbosity mask on a per category basis\&. The format of this value is described in zorp(8)\&. .RE .PP \fB\-\-help\fR or \fB\-h\fR .RS 4 Display this help screen on the options of the \fBlog\fR command\&. .RE .RE .PP \fBszig\fR .RS 4 Display internal information from the specified Zorp instance(s)\&. The information to be disblayed can be specified with the following options: .PP \fB\-\-walk\fR or \fB\-w\fR .RS 4 Walk the specified tree\&. .RE .PP \fB\-\-root [node]\fR or \fB\-r [node]\fR .RS 4 Set the root node of the walk operation to [node]\&. .RE .PP \fB\-\-help\fR or \fB\-h\fR .RS 4 Display a brief help on the options of the \fBszig\fR command\&. .RE .RE .PP \fBhelp\fR .RS 4 Display a brief help message\&. .RE .SH "EXAMPLES" .PP \fBzorpctl start zorp_ftp\fR .PP The command above starts the zorp instance named \fIzorp\-ftp\fR with parameters described in the instances\&.conf file\&. .SH "FILES" .PP The default location for instances\&.conf is /etc/zorp/instances\&.conf\&. .SH "AUTHOR" .PP This manual page was written by the BalaBit Documentation Team \&. .SH "COPYRIGHT" .PP Copyright \(co 2006 BalaBit IT Security Ltd\&. All rights reserved\&. For more information about the legal status of this document please read: \m[blue]\fBhttp://www\&.balabit\&.com/products/zorp/docs/legal_notice\&.bbq\fR\m[] zorp-3.9.5/doc/man/zorpctl.conf.5000066400000000000000000000147261172670260400165700ustar00rootroot00000000000000'\" t .\" Title: zorpctl.conf .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.75.2 .\" Date: 03/06/2012 .\" Manual: [FIXME: manual] .\" Source: [FIXME: source] .\" Language: English .\" .TH "ZORPCTL\&.CONF" "5" "03/06/2012" "[FIXME: source]" "[FIXME: manual]" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" zorpctl.conf_ \- zorpctl(8) configuration file\&. .SH "DESCRIPTION" .PP The zorpctl\&.conf file describes various global options ifluencing the behavior of \fB \fR\fBzorpctl(8)\fR\fB \fR\&. \fBzorpctl(8)\fR processes the file line by line, each line having the structure described below\&. Empty lines and lines beginning with \*(Aq#\*(Aq are comments and are ignored\&. .SH "STRUCTURE" .PP \fBvariable name = variable value\fR .PP Each non\-empty line specifies a variable name and its value separated by the equal sign (\*(Aq=\*(Aq)\&. The following variables are available: .PP \fBAUTO_RESTART\fR .RS 4 Enable the automatic restart feature of \fBzorpctl\fR\&. Instances in auto\-restart mode are restarted automatically when they exit\&. Default value: 1 (TRUE)\&. .RE .PP \fBAUTO_RESTART_TIME_THRESHOLD\fR .RS 4 If a restarted instance exits within this interval (specified in seconds), the restart attempt is considered a failure\&. Default value: 60 seconds\&. .RE .PP \fBAUTO_RESTART_MAX_COUNT\fR .RS 4 Maximum number of restart attempts\&. If the instance is not successfully restarted from \fIAUTO_RESTART_MAX_COUNT\fR attempts, the event is logged\&. Default value: 3\&. .RE .PP \fBAUTO_RESTART_DELAY\fR .RS 4 Wait \fIAUTO_RESTART_DELAY\fR seconds before attempting to restart the Zorp instance\&. .RE .PP \fBSTOP_CHECK_DELAY\fR .RS 4 The rate (delay in seconds) to check a stopping Zorp instance at\&. Default value: 1\&. .RE .PP \fBSTOP_CHECK_TIMEOUT\fR .RS 4 The number of seconds to wait for a stopping Zorp instance\&. Default value: 3\&. .RE .PP \fBSTART_CHECK_TIMEOUT\fR .RS 4 In \fIauto\-restart\fR mode there is no real way to detect whether Zorp failed to load or not\&. Zorpctl waits \fISTART_CHECK_TIMEOUT\fR seconds and assumes that Zorp loaded successfully if it did not exit within this interval\&. Default value: 5 seconds\&. .RE .PP \fBSTART_WAIT_TIMEOUT\fR .RS 4 In \fIno\-auto\-restart\fR mode the successful loading of a Zorp instance can be verified by instructing Zorp to daemonize itself and waiting for the parent to exit\&. This parameter specifies the number of seconds to wait for Zorp to daemonize itself\&. Default value: 60 seconds\&. .RE .PP \fBPROCESS_LIMIT_MIN\fR .RS 4 The minimum process limit (\fIulimit \-u\fR) used by Zorp in the case when the process limit (calculated from the \fI\-\-threads\fR parameter) would result a lower value\&. Default value: 256\&. .RE .PP \fBPROCESS_LIMIT_RESERVE\fR .RS 4 The number of extra processes to be allocated (e\&.g\&.: for proxy modules that are known to spawn new processes)\&. Default value: 64\&. This parameter is added to the regular (calculated as the sum of the processes of a program allowed to run per user) process limit\&. .RE .PP \fBFD_LIMIT_THRESHOLD\fR .RS 4 The expected maximal number of file descriptors openened by the threads\&. The global fd limit is \fIFD_LIMIT_THRESHOLD\fR multiplied by the thread limit\&. Default value: 64\&. .RE .PP \fBFD_LIMIT_MIN\fR .RS 4 The minimum fd limit (\fIulimit \-n\fR) used by Zorp in the case when the process limit (calculated from the \fI\-\-threads\fR and \fIFD_LIMIT_THRESHOLD\fR parameters) would result a lower value\&. Default value: 1024\&. .RE .PP \fBZORP_APPEND_ARGS\fR .RS 4 Zorp\-specific arguments to be appended to the command line of each Zorp instance\&. Also recognised as \fIAPPEND_ARGS\fR (deprecated)\&. Default value: \fI""\fR\&. .RE .PP \fBZORPCTL_APPEND_ARGS\fR .RS 4 Zorpctl\-specific arguments to be appended to the command line of each instance\&. Default value: \fI""\fR\&. .RE .PP \fBCHECK_PERMS\fR .RS 4 Specifies whether to check the permissions of the Zorp configuration directory\&. If set, Zorp refuses to run if the /etc/zorp directory can be written by user other then \fIzorp\fR Default value: 1 (TRUE)\&. .RE .PP \fBCONFIG_DIR\fR .RS 4 The path to the Zorp configuration directory to check if CHECK_PERMS is enabled\&. NOTE: it does not change the Zorp policy file argument, this parameter is only used by the permission validating code\&. Default value: ${prefix}/etc/zorp \&. .RE .PP \fBCONFIG_DIR_OWNER, CONFIG_DIR_GROUP, CONFIG_DIR_MODE\fR .RS 4 The owner/group/permissions values considered valid for the configuration directory\&. \fBzorpctl\fR fails if the actual owner/group/permissions values conflict the ones set here\&. Default values: \fIroot\&.zorp, 0750\fR \&. .RE .PP \fBPIDFILE_DIR\fR .RS 4 The path to the Zorp pid file directory\&. The directory is created automatically prior to starting Zorp if it does not already exist\&.It is created if it does not exist, before NOTE: No \fI\-\-pidfile\fR argument is passed to Zorp, only texistance of the directory is verified\&. Default value: /var/run/zorp\&. .RE .PP \fBPIDFILE_DIR_OWNER, PIDFILE_DIR_GROUP, PIDFILE_DIR_MODE\fR .RS 4 The owner/group/permission values the pidfile directory is created with if it does not exist\&. Default values: \fIroot\&.root, 0700\fR\&. .RE .SH "FILES" .PP .PP The default location for zorpctl\&.conf is /etc/zorp/zorpctl\&.conf\&. .SH "AUTHOR" .PP This manual page was written by the BalaBit Documentation Team \&. .SH "COPYRIGHT" .PP Copyright \(co 2006 BalaBit IT Security Ltd\&. All rights reserved\&. For more information about the legal status of this document please read: \m[blue]\fBhttp://www\&.balabit\&.com/products/zorp/docs/legal_notice\&.bbq\fR\m[] zorp-3.9.5/doc/zorp-tutorial.html000066400000000000000000001246011172670260400170210ustar00rootroot00000000000000
Zorp Tutorial



Version 1.0.4
22th April, 2005


1. Introduction

This tutorial serves as an introduction to setting up a Zorp based firewall on a GNU/Linux distribution. Zorp is a GPLd proxy firewall implementation with the following features:
  • deep protocol analysis (FTP, HTTP, SSL, telnet, finger, whois, plug is included
    in the GPLd version)
  • flexible decision engine, scriptable in Python
  • true modularity, proxies can extend each other

It is assumed that you have general knowledge about IP networks, you know the differences between packet filtering and proxy firewalls and at last but not at least you know how to compile a kernel.
This tutorial covers Zorp 3.0, but the contents might apply to other Zorp releases.

1.1. Further readings

You might also want to read the following HOWTO documents:
HOWTO documents are made available by the Linux Documentation Project at
http://www.linuxdoc.org/

2. Installing the operating system

Zorp currently requires a Linux based operating system because of the kernel extensions it currently needs are available for the Linux kernel only.

There are basically two ways to install an operating system suitable for running Zorp:

  1. Use our customized version of Debian (dubbed ZorpOS) which already contains the required patches for Zorp, or
  2. Use your favourite Linux distribution and start hand-patching about a dozen packages.
2.1. Installing Zorp on ZorpOS

As we don't have an installation program for the GPLd version of ZorpOS itself, you'll need to start with a Debian GNU/Linux woody (version 3.0) installer. Install a minimal system with only the base packages, set up /etc/apt/sources.list to point to our APT repositories while removing the original Debian sources and do an "apt-get dist-upgrade". The address of our Debian package repository can be found on our Zorp upgrades page.

Make sure that you install a new kernel image that matches your architecture. The name of the package depends on your architecture and whether you are multiple processors in your computer. The package is called kernel-image-2.4.28-zorpos-<arch> with an appended "-smp" for SMP support. For example a pentium4 based box with a single CPU, you will want to install "kernel-image-2.4.28-zorpos-pentium4". The following architectures are available: pentium, pentium4, athlon.

At the end you will find yourself in ZorpOS, kernel, iptables and Zorp dependencies properly patched and ready to run Zorp itself.

2.2. Installing Zorp on your favourite distribution

This method is somewhat more difficult and requires expertise to patch, build and install programs from source.

Pick your favourite Linux distribution and install a bare-bones system with only the absolutely necessary components you need.

You need to compile a couple of programs and the kernel itself, but a compiler is something that should not be installed on the firewall. Therefore it is best to compile stuff on a separate host and copy only the binaries to the firewall. When compiling on a different host, make sure that the libraries that you will link Zorp against have the same version on the compiling host and the firewall.

Be sure to install build-time dependencies so Zorp will find them later:

  • GLib 2.2, however there are some outstanding problems which we fixed in our GLib packages, check out our Debian package sources at: http://www.balabit.com/downloads/zorp/zorp-os/pool/g/glib2.0. Some of those fixes are included in GLib 2.4.x.
  • Python 2.3 (Zorp 3.0.x requires this Python version, Zorp 2.0/2.1 uses Python 2.1, Zorp 1.4 originally used Python 1.5.2 (If you have multiple versions of python development environment, make sure that the default version of python and python-extclass is the same!)
  • libcap 1.10 (Zorp optionally manages its own capabilities, dropping unneeded caps if possible)
  • openssl 0.9.7d or later

Zorp works with either a Linux 2.2 or Linux 2.4 kernel, but neither of those is usually compiled as required in distributions. Thus you will need to compile your own kernel, see the next section.

3. Compiling your own kernel

Zorp is a transparent proxy firewall, thus it needs a couple of kernel extensions. Either Linux 2.2 or Linux 2.4 will work but the transparent proxy features are somewhat different. If you are using our ZorpOS repositories you can skip this section as the kernel in that repository is already patched with TProxy.

In addition to transparent proxying you might want to add a security patch like openwall or grsecurity as the firewall is a security sensitive device.

3.1. Transparent proxying in Linux 2.2

Linux 2.2 features built in transparent proxy capabilities, but you need to enable them in your kernel configuration. It can be found under the "Networking options" menu, the option is called "Transparent proxy support". This option requires that you turn on "IP: firewalling" option as well.

You might also want to enable policy routing and other advanced IP features as they are often needed on firewalls.

3.2. Transparent proxying in Linux 2.4/2.6

The transparent proxy support that was present in Linux 2.2 was removed from Linux 2.4 when iptables was introduced. We implemented a patch against Linux 2.4 that adds the required features so Zorp tightly integrates into NetFilter/iptables.

You can download this patch from http://www.balabit.com/products/oss/tproxy/

After you add this patch, enable iptables, iptables connection tracking, iptables nat and iptables transparent proxying options in your kernel configuration. The target 'TPROXY' and the match 'tproxy' is especially important, other iptables modules should be compiled as necessary.

Zorp used to detect TProxy functions by attempting to load the iptable_tproxy module. This is not true as of Zorp 3.0, Zorp will try to autoload the module but will detect TProxy if the loading fails.

There are two incompatible versions of TProxy, TProxy 1.2.x is compatible with all current Zorp versions but does not work on Linux 2.6 due to a colliding setsockopt number. TProxy 2.0 works on Linux 2.6 but not all Zorp versions support it yet. (Zorp 3.0 is ok and Zorp 2.1 starting from 2.1.8.2, that is the second test release of Zorp 2.1.9)

In addition to compiling the kernel you will also need to compile the iptables userspace program to include the TPROXY and tproxy modules.

4. Compiling Zorp

It is generally good not to have a compiler on your firewall host, so either compile the package on a different host, or remove gcc and development files from your firewall after installation.

In Zorp 2.0 the core zorp tarball was split into two: a library called libzorpll containing the low level functions and Zorp itself.

You will first need to compile libzorpll:

# tar xvfz libzorpll-3.0.6.0.3.tar.gz
# cd libzorpll-3.0.6.0.3
# ./configure
# make
# sudo make install #(assuming sudo is the command to make you root)

Make sure that you copy the resulting shared library to your firewall host. This can be accomplished by using the DESTDIR make variable:

# sudo make DESTDIR=/tmp/staging install

This command will use /tmp/staging as a root directory while copying files, thus /usr/lib/libzorpll.so is copied to /tmp/staging/usr/lib/libzorpll.so.

At the end of the compilation you can simply copy the contents of your staging directory to the firewall host.

Alternatively you can compile libzorpll to a Debian package by entering "dpkg-buildpackage" in the extracted source directory. The build process results in two Debian packages: libzorpll__i386.deb and libzorpll-dev__i386.deb assuming you are compiling on an Intel architecture. Install both debs on your compiling host, and libzorpll on your firewall host, as development files are needed only for compilation.

If libzorpll was successfully compiled you can go on to compile Zorp itself:

# tar xvfz zorp-3.0.3.2.tar.gz
# cd zorp-3.0.3.2
# ./configure
# make
# sudo make DESTDIR=/tmp/staging install

This will compile zorp and copy the resulting binaries to /tmp/staging. The configure script checks your system whether it finds the required build dependencies. If one of the dependencies are not met, try to install the missing package. In addition to what is described in section 2 as Zorp requirements, you will also need libzorpll. Please note that some libraries are located with the GNOME pkg-config mechanism which installs library meta-information to so-called .pc files. pkg-config uses the PKG_CONFIG_PATH environment variable to locate these files and you might set it properly for the configure script to find libzorpll.

It might be possible that the configure script does not find some of the required libraries even if they are installed. The biggest problem usually is the Python development files. Zorp looks for the shared library version of Python and it is not always provided by distributions. In this case you might try to use the '--with-python-headers' and '--with-python-libraries' configure options or ask for help on the mailing list.

Of course the trick for building Debian packages is possible again by entering "dpkg-buildpackage" in the extracted source directory. It will result in the following debs to be created:

  • zorp: the main program
  • zorp-dev: development files needed to compile zorp modules
  • zorp-modules: proxy modules
  • zorp-doc: documentation files

From these only zorp and zorp-modules is required to be installed on your firewall host.

5. Starting up Zorp

Assuming the build process was successful and you copied the necessary files to your firewall host, you can now start configuring Zorp itself.

5.1. Sample network topology

In the following sections I am trying to guide you through Zorp configuration by using a simple example. This example network has three distrinct security zones:

  1. an intranet where protected client computers reside
    • address range: 192.168.0.0/24
    • firewall IP: 192.168.0.254
    • clients are permitted to use HTTP, HTTPS and FTP services destined to any
      other zones (internet, DMZ)
    • clients are permitted to use SMTP, DNS and NTP installed on the firewall
  2. a demilitarized zone or DMZ on another interface where public access services are provided from (the web server of the company)
    • address range: 10.0.0.0/24
    • firewall IP: 10.0.0.254
    • web server IP: 10.0.0.1
    • clients are not permitted to use any service outbound
    • clients are permitted to use SMTP, DNS and NTP installed on the firewall
  3. the internet itself with a single, static IP address
    • firewall IP: 11.12.13.14
    • clients are permitted to use HTTP serviced in the DMZ
    • clients are permitted to use SMTP installed on the firewall
    • the firewall must communicate with the NTP server on the internet and also to post DNS requests to a single forwarder
5.2. Architecture

Zorp is a proxy based firewall which means that it has several protocol implementations which each take care about mediating a given protocol between hosts on its different interfaces.

Zorp based firewalls are usually integrated into the network topology as routers, this means that they have an IP address in all their subnets, and hosts on different subnets use the firewall as their gateway to the outside world.

Although proxy based, Zorp uses a packet filter to preprocess the packet stream, and also to provide transparency.

A TCP session is established in the following way:

  1. the client initiates a connection by sending a TCP SYN packet destined to the server
  2. the firewall behaves as a router between the client and the server, receives the SYN packet on one of its interfaces and consults the packet filter
  3. the packet filter rulebase is checked whether the given packet is permitted
  4. if the given connection is to be processed by a proxy, then the packet filter rulebase contains a REDIRECT (ipchains) or TPROXY (iptables) target. Both REDIRECT and TPROXY requires a port parameter which tells the local port of the firewall host where the proxy is listening.

    It is also perfectly possible although strongly discouraged to bypass the proxies and forward packets directly, you only need to use the ACCEPT target instead of TPROXY.
  5. Zorp accepts the connection, checks its own access control rules and starts the appropriate proxy
  6. the proxy connects to the server on its own as needed (the server side connection is not necessarily established immediately)
  7. the proxy mediates protocol requests and responses between the communicating hosts while analyzing the ongoing stream

Of course the remaining packets of the TCP session after the initial SYN must also be allowed by the packet filter.

5.3. Configuring network interfaces

As I stated earlier a Zorp based firewall fulfills the role of an IP router from its neighbour perspective. This means that all its interfaces must be configured to have an IP address in the subnet of the connecting network.

The firewall has three interfaces:

  • eth0 as the intranet interface with IP 192.168.0.254/24
  • eth1 as the DMZ interface with IP 10.0.0.254/24
  • eth2 as the internet interface with IP 11.12.13.14
NOTE: that the transparent proxy patch for Linux 2.4 requires a local address which does not collide with any local address in your network. The best way to provide one is to configure a dummy0 interface with a dummy IP address in the RFC1918 reserved range. You will need to pass this IP to Zorp using the --autobind-ip command line option. See the TPROXY patch documentation for more information.

(http://www.balabit.com/products/oss/tproxy/README.txt)

5.4. Configuring the packet filter

To configure the packet filter we first need to establish a couple of rules we will be adhering to, as the packet filter ruleset can become quite complicated. First of all we name all the neighbouring networks. This name should be short and easy to remember. These names will be used when naming chains.

Long nameShort name
Intranetintra
Internetinter
DMZdmz

The iptables subsystem defines several tables each with its own set of chains and rules. We will be focusing on two tables now: the filter table where simple packet filtering is done, and the tproxy table where we are redirecting sessions to our proxies.

5.4.1. Storing the ruleset

Some people like storing their ruleset as a shell script which invokes the necessary iptables commands. As I don't like mixing executable code and data we use the format defined by iptables-save & iptables-restore.

As raw iptables-restore format has no macro possibility we created a frontend named iptables-utils where a couple of scripts help the creation and maintenance of a packet filter rulesets. Here's an outline of the iptables-utils approach:

  • the following files are used by iptables-utils:
    • iptables.conf.in: contains our ruleset before processing, this is a user supplied file, we are going to edit this with our favourite editor
    • iptables.conf.var: contains our macro definitions, it might contain a series of C like #define statements. I say C like because macro substition differs from cpp.
    • iptables.conf.new: when processing conf.in & conf.var our new ruleset will be generated here
    • iptables.conf: is our current ruleset, iptables.conf.new is copied here if found to be correct
  • the ruleset is maintained the following way:
    • you edit either iptables.conf.in or iptables.conf.var
    • you process your modifications by the command 'iptables-gen', this will result in a iptables.conf.new to be generated
    • you test your new ruleset by invoking 'iptables-test', this script loads the new ruleset, waits a couple of seconds and reloads the old ruleset, if you made a mistake you are still not closed out from the system
    • if the new ruleset is ok, you invoke 'iptables-commit' which overwrites iptables.conf with iptables.conf.new and loads the ruleset

Using iptables-utils was absolutely beneficial in the long term as the number of system-closeouts dramatically decreased, which is good if you are hundreds of miles of away from the firewall.

Macro expansion is not simple substition, if a macro contains several words the rule where the macro is referenced is copied, at the end you get a new rule for each word in your macro. For instance:

iptables.conf.var:

         #define SSH_PERMITTED 1.2.3.4 1.2.3.5

iptables.conf.in:

         -A INPUT -p tcp -m tcp -s SSH_PERMITTED --dport 22 -j ACCEPT

You will get two rules the first with 1.2.3.4 substituted, the second with 1.2.3.5 substituted.

5.4.2. Naming the chains

In addition to the standard chains provided by iptables (INPUT, OUTPUT etc) we will create separate chains for each security zone. Each security zone will have two chains:

  • a chain which contains rules for traffic which passes the firewall
  • a chain which contains rules for traffic destined to the firewall

The first one will be prefixed by PR which stands for PRoxy rules, the second one will be prefixed by LO which stands for LOcal rules. Proxy rules will be placed into the 'tproxy' table, local rules will be placed into the 'filter' table. If we assume that all traffic goes through proxies we won't need NAT nor mangle rules. (of course we can add further finetuning to our rulebase, like limiting the number of SYNs etc)

5.4.3. Jumping to our chains

We have two set of chains for each security zone, LOxxx chains are processed in the filter table, INPUT chain. PRxxx chains are processed in the tproxy table, PREROUTING/OUTPUT chain.

Our filter/INPUT chain will be something like this:

     ...
     -A INPUT -m tproxy -j ACCEPT
     -A INPUT -i <intranet iface> -j LOintra
     -A INPUT -i <internet iface> -j LOinter
     -A INPUT -i <dmz iface>      -j LOdmz
     -A INPUT -j DROP

This means that all permitted traffic must be enabled in their specific chain or will be dropped on the INPUT chain. Of course logging dropped packets would be a good idea. It is important to mention that our FORWARD chain should contain a single DROP rule as we don't forward packets. Each LOxxx chain should look like this:

     -A LOintra -p tcp --dport 22 -j ACCEPT
     ... permit each service ... 
     -A LOintra -j DROP

Of course our LOxxx chains might be different for each zone, as we might permit SSH access from the intranet only.

Note the '-m tproxy' rule at the front of other rules, it allows all traffic redirected by any TPROXY feature to pass the filter table. (this includes TPROXY redirections, and foreign-bound traffic)

We took care about local services provided by the firewall, let's make our proxying rules now.

Our tproxy/PREROUTING chain will be something like this:

     -A PREROUTING -i <intranet iface> -d ! <fw intranet IP> -j PRintra
     -A PREROUTING -i <internet iface> -d ! <fw internet IP> -j PRinter
     -A PREROUTING -i <dmz iface>      -d ! <fw dmz IP>      -j PRdmz

A PRxxx chain should something like this:

     -A PRintra -d 0/0 --dport 80 -j TPROXY --on-port 50080
     ... repeat the above rule for each service ...

At the end of a PRxxx chain no DROP should be performed, as unmodified sessions will be stopped when the filter table is evaluated. The port number specified by TPROXY rules should match the port number where the transparent proxy (Zorp in our example) will be bound.

Here is a complete iptables configuration for our sample network:
iptables.conf.var:

#define IFintra   eth0
#define NETintra  192.168.0.0/24

#define IFinter   eth1

#define IFdmz     eth2
#define NETdmz    10.0.0.0/24

#define NTP_SERVERS 1.2.3.4 1.2.3.5

#define DNS_SERVERS 2.3.4.5

iptables.conf.in:
*tproxy
:PREROUTING ACCEPT
:OUTPUT ACCEPT
:PRintra -
:PRinter -
:PRdmz -
-A PREROUTING -i IFintra -j PRintra
-A PREROUTING -i IFinter -j PRinter
-A PREROUTING -i IFdmz   -j PRdmz
// PRintra chain
-A PRintra -p tcp --dport 80 -j TPROXY --on-port 50080
-A PRintra -p tcp --dport 443 -j TPROXY --on-port 50443
-A PRintra -p tcp --dport 21 -j TPROXY --on-port 50021
// PRinter chain
-A PRinter -p tcp --dport 80 -j TPROXY --on-port 50080
// PRdmz chain
// no services permitted
COMMIT
*filter
:INPUT DENY
:FORWARD DENY
:OUTPUT ACCEPT
:noise -
:spoof -
:spoofdrop DROP
:LOintra -
:LOinter -
:LOdmz -
-A INPUT -j noise
-A INPUT -j spoof
// permit all traffic initiated by transparent proxies
-A INPUT -m tproxy  -j ACCEPT
//
// permit all TCP traffic initiated by local processes, or allowed by rules
// below, we don't trust the state match for UDP traffic, they will be handled
// by individual rules below.
//
-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
// permit all loopback traffic
-A INPUT -i lo -j ACCEPT
-A INPUT -i IFintra -j LOintra
-A INPUT -i IFinter -j LOinter
-A INPUT -i IFdmz   -j LOdmz
-A INPUT -j DROP
-A FORWARD -j LOG --log-prefix "FORWARD DROP: "
-A FORWARD -j DROP
// LOintra
-A LOintra -p udp --dport 53 -j ACCEPT
-A LOintra -p udp --dport 123 -j ACCEPT
-A LOintra -p tcp --syn --dport 25 -j ACCEPT
-A LOintra -j LOG --log-prefix "LOintra DROP: "
-A LOintra -j DROP
// LOinter
// permit DNS replies, bind is configured to send out DNS packets from this
// port. We could also use the state match in our INPUT chain.
-A LOinter -p udp -s DNS_SERVERS --dport 53000 -j ACCEPT
-A LOinter -p udp -s NTP_SERVERS --dport 123 -j ACCEPT
-A LOinter -p tcp --syn --dport 25 -j ACCEPT
-A LOinter -j LOG --log-prefix "LOinter DROP: "
-A LOinter -j DROP
// LOdmz
-A LOdmz -p udp --dport 53 -j ACCEPT
-A LOdmz -p udp --dport 123 -j ACCEPT
-A LOdmz -p tcp --syn --dport 25 -j ACCEPT
-A LOdmz -j LOG --log-prefix "LOdmz DROP: "
-A LOdmz -j DROP
//
// noise chain, should drop all packets which need not be logged,
// otherwise it should return to the main ruleset
//
-A noise -p udp --dport 137:139 -j DROP
-A noise -j RETURN
//
// spoof chain, should drop all packets with spoofed source address
// otherwise it should return to the main ruleset
//
-A spoof -i lo -j RETURN
-A spoof ! -i lo -s 127.0.0.0/8 -j spoofdrop
-A spoof -i IFintra ! -s NETintra -j spoofdrop
-A spoof ! -i IFintra -s NETintra -j spoofdrop
-A spoof -i IFdmz ! -s NETdmz -j spoofdrop
-A spoof ! -i IFdmz -s NETdmz -j spoofdrop
-A spoof -j RETURN
//
-A spoofdrop -j LOG --log-prefix "Spoofed packet: "
-A spoofdrop -j DROP
COMMIT

5.5. Configuring Zorp

This section focuses on Zorp configuration.

5.5.1. Zorp & Python

The configuration of Zorp is Python based, in fact the configuration file is a Python module in itself. This does not mean however that the administrator would have to learn Python and does neither mean that Zorp itself is written in Python.

The use of Python is twofold:

  1. it is used as a glue to connect Zorp components together

    These parts are implemented by us and live as Python modules in the directory '/usr/share/zorp/pylib'.

  2. it is used to describe the configuration and to customize proxy behaviour

    This part is written by the administrator, but an effort was made to make the configuration file look like configuration and _NOT_ a program. A standard policy without tricks is easier to write than a 'netperm-table' (of TIS fwtk fame).

Though the configuration file may not seem like a Python module, it is important to know it is parsed as one. So the following syntactical requirements of Python apply:

Indentation is important as it marks the beginning of a block, similar to what braces do in C/C++/Java. This means that the way you indent blocks must be consistent for that given block. For example this is correct:

    if self.request_url == 'http://www.balabit.hu/':
        print('debug message')
        return HTTP_REQ_ACCEPT
    return HTTP_REQ_REJECT

This is not:

    if self.request_url == 'http://www.balabit.hu/':
          print('debug message')
        return HTTP_REQ_ACCEPT
    return HTTP_REQ_REJECT

The code snippet above could be expressed in a C-like language like this:

    if (self.request_url == 'http://www.balabit.hu/')
      {
        print('debug message');
        return HTTP_REQ_ACCEPT;
      }
    return HTTP_REQ_REJECT;


5.5.2. Zorp components

To start configuring Zorp you will need to know the following Zorp components:

  • Instance: it is possible to start several instances of Zorp just like you can start many instances of any program. Each zorp instance has a name and its own set of services to provide. Several instances can use the same configuration file, though each will process only the relevant parts.
  • Zone: A zone encapsulates a part of the neighbouring network. Each client and server is a member of exactly one zone, membership based on IP address. Zorp uses a zone based access control which means that the permitted set of services available to a client/server combination is assigned to the zones those clients and services reside in.
  • Service: a service encapsulates a proxy and associated parameters. Each service is identified by a unique name which is used for logging and access control purposes.
  • Listener: a listener is an object which listens for connection on a given port and for each accepted TCP session it is capable of starting service instances. Listeners are the input point of Zorp, usually the packet filter redirects TCP sessions to one of the ports where a Zorp Listener is waiting.
  • Router: a router in Zorp decides the destination of a given session. Each service has an associated Router but as it defaults to TransparentRouter it does not have to be explicitly given.
  • Chainer: a chainer is used even less often than a Router, it is also associated with services and their task is to establish the server side connections of proxies.
5.5.3. The simplest Zorp configuration

Zorp uses two files to store its configuration. The file named 'instances.conf' contains the list of Zorp instances to be run. Its content is processed by the 'zorpctl' script. The other file, usually named 'policy.py' stores the policy (aka ruleset) of one or more Zorp instances.

The following listing is a complete, working Zorp policy file with a single instance named 'intra', and a single zone named 'inter' which encapsulates the whole IPv4 address space.

from Zorp.Core import *

InetZone('inter', '0.0.0.0/0')

def intra():
	pass

A few things to notice:
  • this file is a Python module, therefore the import statement on the first line, it imports all core Zorp symbols that are required even for basic operation.
  • the name of our zone here matches the name we used while writing our packet filter ruleset, this is not a requirement, it is just good practice to make your firewall ruleset cleaner.
  • the instance named 'intra' is represented as a Python function with no arguments. This is currently empty, thus the Python NOP called 'pass' as the function body (Python requires at least one statement in every block). You will see how this can be augmented with Service and Listener definitions so 'pass' will not be needed.

Zorp instances can be started and stopped by the 'zorpctl' program (it used to be a script until Zorp 2.1, but is a C program Zorp 3.0 onwards). 'zorpctl start' starts all known instances, 'zorpctl stop' stops them. 'zorpctl' works by parsing the '${prefix}/etc/zorp/instances.conf' file. Each instance name in the instances.conf file must have a corresponding instance definition in the policy file to work correctly. A sample instances.conf file will be shown in the following paragraphs.

This is simple enough, isn't it? Now let's augment it with the definitions of our zones, and let's create three instances for each of our zones:

 
from Zorp.Core import *


InetZone('intra', '192.168.0.0/24')
InetZone('dmz', '10.0.0.0/24')
InetZone('inter', '0.0.0.0/0')

def intra():
	pass

def dmz():
	pass

def inter():
	pass

You will need the following instances.conf(5) file to start your zorp instances using zorpctl:

intra -v3 -p /etc/zorp/policy.py --autobind-ip 192.168.0.1
inter -v3 -p /etc/zorp/policy.py --autobind-ip 192.168.0.1
dmz -v3 -p /etc/zorp/policy.py   --autobind-ip 192.168.0.1

The 'instances.conf' file specifies Zorp startup parameters to use when the given instance is started. Consult zorp(8) manpage or run '/usr/lib/zorp/zorp --help' for more details.

One important point to make is the 'autobind-ip' argument in the example above, TPROXY requires a local, non-routeable IP address to make transparency possible. See section 5.4 and the TPROXY README file for more details.

5.5.4. Adding our services

Although our Zorp process is running by entering the configuration in the previous section, it would do nothing really useful. To do anything useful we have to define services, and listeners.

from Zorp.Core import *
from Zorp.Http import *

InetZone('intra', '192.168.0.0/24',
	 outbound_services=['intra_HTTP'])
InetZone('dmz', '10.0.0.0/24',
	 inbound_services=['intra_HTTP'])
InetZone('inter', '0.0.0.0/0',
	 inbound_services=['intra_HTTP'])

def intra():
	Service('intra_HTTP', HttpProxy)
	Listener(SockAddrInet('192.168.0.254', 50080), 'intra_HTTP')

def dmz():
	pass

def inter():
	pass
A few things to notice:
  • we have added a new import line to import all symbols the HTTP module provides, the most important being HttpProxy which we used in our service definition.
  • we have added access control information to our zones, the 'intra_HTTP' service is permitted to be used outbound from the zone 'intra', and is permitted to target servers in the zones 'dmz' and 'inter'.
  • we have removed the 'pass' statement from our 'intra' function and added two statements instead: a service definition and a listener definition
  • The service definition names our new service 'intra_HTTP' which is using the proxy named HttpProxy, further options could be specified here as you will see in coming sections.
  • The listener opens the port 192.168.0.254:50080, our packet filter rules redirect all transparent, port 80 traffic to this port
  • The listener starts the service named in its second argument.
  • Service names are divided into three parts separated by an underscore: source zone, protocol, destination zone. If the service is transparent and the destination is not known, the destination zone is omitted from the service name. This naming scheme is not required by Zorp, though the use of some kind of scheme makes firewall administration easier.

Here is a complete listing of the simple policy I presented in section 5.1.

from Zorp.Core import *
from Zorp.Plug import *
from Zorp.Http import *
from Zorp.Ftp import *

InetZone('intra', '192.168.0.0/24',
	 outbound_services=['intra_HTTP', 'intra_HTTPS', 'intra_FTP'])
InetZone('dmz', '10.0.0.0/24',
	 inbound_services=['intra_HTTP', 'inter_HTTP_dmz'])
InetZone('inter', '0.0.0.0/0',
	 outbound_services=['inter_HTTP_dmz'],
	 inbound_services=['intra_HTTP', 'intra_HTTPS', 'intra_FTP'])

def intra():
	Service('intra_HTTP', HttpProxy)
	Listener(SockAddrInet('192.168.0.254', 50080), 'intra_HTTP')

	Service('intra_HTTPS', PlugProxy)
	Listener(SockAddrInet('192.168.0.254', 50443), 'intra_HTTPS')

	Service('intra_FTP', FtpProxy)
	Listener(SockAddrInet('192.168.0.254', 50021), 'intra_FTP')

def dmz():
	pass

def inter():
	Service('inter_HTTP_dmz', HttpProxy,
		router=DirectedRouter(SockAddrInet('10.0.0.1', 80)))
	Listener(SockAddrInet('11.12.13.14', 50080), 'inter_HTTP_dmz')

A few things to notice:

  • we have added two new import statements to import the symbols provided by the Plug and Ftp proxies.
  • we used a PlugProxy for HTTPS purposes, we could also have used an SSL proxy instead
  • our 'inter_HTTP_dmz' service has a fixed destination, this is accomplished by using an explicit router specification: we use DirectedRouter() to specify the destination server. All other services use the default router named TransparentRouter() which means they connect to the original destination of the client.
  • the 'inter_HTTP_dmz' service has a fully qualified name, since we know the destination zone as - unlike other services - it has a fixed, predefined destination: it connects to the webserver in the DMZ.

5.5.5. Customizing proxies

In the previous section we implemented a firewall policy in about 30 lines. Although our example was quite simple there are real world firewalls with policies not more difficult than our sample.

Until we did not really use the fact that we have a programming language in our hands. The configuration above is simple, but it doesn't show the potential Zorp provides.

The second argument of a Service statement is a proxy class, the fact it is a class makes easy customization possible. As customization requires a bit more knowledge about Python, we provided a good number of predefined proxy classes. As an example Ftp has a couple of predefined variations:

  • FtpProxyRO,
    an FTP proxy which permits downloading only

  • FtpProxyAnonRO,
    an FTP proxy which permits downloading only, and only the anonymous user
    is permitted

If you cannot find the necessary customization, then - and only then - do you need to derive your own class. The next listing shows how.

class HttpProxyAnonimize(HttpProxy):
	def config(self):
		HttpProxy.config(self)
		# customization statements

The listing above shows a class definition in Python, our new class has the name 'HttpProxyAnonimize', it is derived from HttpProxy and has defined the method named 'config'. The 'config' method calls the 'config' method in our superclass to also derive default configuration settings. You can take the above code snippet as a skeleton for your future customizations. Changing the parent class to 'FtpProxy' and making our 'config' method to call the config method from 'FtpProxy' would create a customized Ftp proxy class.

What can you put in your 'config()' method? Anything that the proxy provides. Our HTTP proxy has over 30 settings and there are complex filtering rules that you can set. Documentation on each attribute a given proxy provides can be found in the Python module for that proxy. This means that the documentation for our Http proxy can be found in /usr/share/zorp/pylib/Zorp/Http.py. The documentation usually also contains examples.

Now, let us create an Http proxy that hides the browser type:

class HttpProxyAnonimize(HttpProxy):
	def config(self):
        	HttpProxy.config(self)
                self.request_headers["User-Agent"] = \
			(HTTP_HDR_CHANGE_VALUE, "Mozilla 5.0")

That's it, now you can refer to HttpProxyAnonimize from your service definitions like this:

def intra():
	Service('intra_HTTP', HttpProxyAnonimize)
	Listener(SockAddrInet('192.168.0.254', 50080), 'intra_HTTP')
	
	...

A little bit more complex example shows how to remove Referer information. This is a bit more difficult as a lot of sites relies on Referer being correct, some of them simply stops working if the referer does not point to them. Thus simply changing the referer value to something fixed will not work.

We work around this by setting the referer field to the currently request URL. Let us extend our previous HttpProxyAnonimize with this feature:

class HttpProxyAnonimize(HttpProxy):
	def config(self):
        	HttpProxy.config(self)
                self.request_headers["User-Agent"] = \
			(HTTP_HDR_CHANGE_VALUE, "Mozilla 5.0")
                self.request_headers["Referer"] = \
			(HTTP_HDR_POLICY, self.rewriteReferer)

	def rewriteReferer(self, name, value):
		self.current_header_value = self.request_url
		return HTTP_HDR_ACCEPT

As you can see we have defined a Python function to perform the Referer change. Of course we can do even more complex things by extending the proxy functionality here and there, however this is not the scope of this document.

5.6. What is modularity?

As I have already written in the introduction, Zorp has a modular architecture, proxies can extend each other. But what does this mean exactly?

Each proxy in Zorp is generalized in a way that it is independent of the communication mechanism used towards its client or server peers. This means that proxies don't really care whether they communicate using a real TCP connection or a UNIX domain socket. This comes handy when we want to analyze something that has multiple protocol levels:

For example simple HTTP uses TCP as its transport protocol but does not have authentication or integrity protection on its own. If you add SSL to the picture you get HTTPS: HTTP running on SSL, which in turn runs on TCP. As Zorp has an SSL capable proxy (implementing an MITM in fact), we can construct an HTTPS proxy out of our ordinary HTTP and SSL proxies. Here is an example:

class HttpsProxy(PsslProxy):

       class EmbeddedHttpProxy(HttpProxy):
               def config(self):
                       HttpProxy.config(self)
                       self.request_header["User-Agent"] = (HTTP_HDR_DROP)

       def config(self):
               PsslProxy.config(self)
               self.client_need_ssl = TRUE
               self.client_key_file = '/etc/zorp/https.key'
               self.client_cert_file = '/etc/zorp/https.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_REQUIRED_TRUSTED

               # here we specify that decrypted protocol stream is
               # to be passed to an instance of EmbeddedHttpProxy above
               self.stack_proxy = EmbeddedHttpProxy

A few things to notice:
  • Python syntax is fully recursive, this comes well when defining "EmbeddedHttpProxy", this way we can emphasize that it is embedded into "HttpsProxy". However this is syntactic sugar only, nothing requires you to define embedded proxy classes within other classes, you can use your proven Http filtering class inside SSL.
  • The most important part is the line with the comment, we specify that as soon as the SSL handshakes are completed, an EmbeddedHttpProxy should be started with the decrypted protocol streams. The stacked proxy can do anything to modify protocol contents.
  • The PSSL proxy allows encryption to be enabled or disabled on both of its client or server side, thus it can be used to wrap or unwrap protocol streams into/out of SSL.
  • Of course the proxy above can be fully transparent.
  • Not all proxies provide embedded protocol streams that you can attach other proxies to (PsslProxy and PlugProxy does, FingerProxy does not)

6. Where to look for further information
  • Proxy specific documentation - it is available in inline Python docstrings of each proxy module

  • Python layer -

    a couple of Zorp objects are implemented in pure Python, each of these classes is documented in the appropriate Python module.

  • mailing list -

    last but not at least the mailing list and its archive is a useful resource. we usually respond to questions quite fast.
zorp-3.9.5/lib/000077500000000000000000000000001172670260400132755ustar00rootroot00000000000000zorp-3.9.5/lib/Makefile.am000066400000000000000000000013761172670260400153400ustar00rootroot00000000000000SUBDIRS = . zorp LIBS = @LIBZORP_LIBS@ lib_LTLIBRARIES = libzorp.la libzorp_la_LDFLAGS = \ -version-info $(LIBZORP_LT_CURRENT):0:$(LIBZORP_LT_AGE) \ -release $(LIBZORP_LT_RELEASE) \ -export-dynamic libzorp_la_SOURCES = \ satyr.c pysatyr.c \ pyzasauth.c zasauth.c \ proxy.c proxystack.c modules.c \ pycore.c pysockaddr.c pypolicy.c \ pyproxy.c pystream.c \ zorp.c \ authprovider.c \ tpsocket.c dimhash.c \ szig.c \ pydispatch.c dispatch.c \ attach.c connection.c pyattach.c \ plugsession.c zpython.c \ dgram.c \ pydict.c pystruct.c audit.c \ ifmonitor.c proxygroup.c pyproxygroup.c \ coredump.c notification.c pybalance.c \ pyaudit.c timestamp.c idsconn.c \ proxyssl.c pyx509.c proxysslhostiface.c \ bllookup.c \ kzorp.c \ credentials.c zorp-3.9.5/lib/attach.c000066400000000000000000000200121172670260400147000ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include /* * Attach - establish outgoing connection * */ struct _ZAttach { gchar session_id[MAX_SESSION_ID]; guint proto; ZProxy *proxy; ZSockAddr *bind_addr; ZSockAddr *local; ZSockAddr *remote; ZAttachParams params; ZConnector *connector; ZConnection *conn; gboolean connect_finished; ZAttachCallbackFunc callback; gpointer user_data; GDestroyNotify destroy_data; }; /** * z_attach_callback: * @self this * @conn The connection to add * * Internal callback function, called when a connection is established. * Called from: z_attach_tcp_callback (tcp) or z_attach_start (udp). */ static void z_attach_callback(ZStream *fdstream, GError *err G_GNUC_UNUSED, gpointer user_data) { ZAttach *self = (ZAttach *) user_data; gchar buf[256]; ZConnection *conn; z_session_enter(self->session_id); if (fdstream != NULL) { gint fd = z_stream_get_fd(fdstream); conn = z_connection_new(); if (z_getsockname(fd, &conn->local, 0) != G_IO_STATUS_NORMAL || z_getpeername(fd, &conn->remote, 0) != G_IO_STATUS_NORMAL) { z_connection_destroy(conn, FALSE); z_stream_close(fdstream, NULL); z_stream_unref(fdstream); conn = NULL; goto exit; } conn->protocol = self->proto; conn->stream = fdstream; conn->dest = z_sockaddr_ref(conn->remote); } else { conn = NULL; } /*LOG This message reports that the connection was successfully established. */ z_log(self->session_id, CORE_DEBUG, 6, "Established connection; %s", z_connection_format(conn, buf, sizeof(buf))); exit: if (self->callback) { self->callback(conn, self->user_data); } else { self->conn = conn; self->connect_finished = TRUE; } z_session_leave(self->session_id); } /** * z_attach_start: * @self this * * Initiate establishing a connection * * Returns: * TRUE on success */ static gboolean z_attach_setup_connector(ZAttach *self) { z_session_enter(self->session_id); self->conn = NULL; if (self->proto == ZD_PROTO_TCP) { self->connector = z_stream_connector_new(self->session_id, self->bind_addr, self->remote, (self->params.loose ? ZSF_LOOSE_BIND : 0) | (self->params.random ? ZSF_RANDOM_BIND : 0) | ZSF_MARK_TPROXY, z_attach_callback, self, NULL); } else if (self->proto == ZD_PROTO_UDP) { self->connector = z_dgram_connector_new(self->session_id, self->bind_addr, self->remote, (self->params.loose ? ZSF_LOOSE_BIND : 0) | (self->params.random ? ZSF_RANDOM_BIND : 0) | ZSF_MARK_TPROXY, z_attach_callback, self, NULL); } if (self->connector) { z_connector_set_timeout(self->connector, self->params.timeout < 0 ? -1 : (self->params.timeout + 999) / 1000); z_connector_set_tos(self->connector, self->params.tos); } z_session_leave(self->session_id); return self->connector != NULL; } /** * z_attach_start: * @self this * * Initiate establishing a connection * * Returns: * TRUE on success */ gboolean z_attach_start(ZAttach *self, ZPoll *poll, ZSockAddr **local) { gboolean res = FALSE; ZProxyGroup *proxy_group; GMainContext *context; z_session_enter(self->session_id); if (z_attach_setup_connector(self)) { if (poll) { context = z_poll_get_context(poll); } else if (self->proxy) { proxy_group = z_proxy_get_group(self->proxy); context = z_proxy_group_get_context(proxy_group); } else { context = NULL; } res = z_connector_start_in_context(self->connector, context, &self->local); if (res && local) *local = z_sockaddr_ref(self->local); } z_session_leave(self->session_id); return res; } gboolean z_attach_start_block(ZAttach *self, ZConnection **conn) { ZProxyGroup *proxy_group; gboolean res = FALSE; g_assert(self->callback == NULL); g_assert(self->connector == NULL); *conn = NULL; if (self->proxy && self->proxy->flags & ZPF_NONBLOCKING) { if (z_attach_start(self, NULL, NULL)) { proxy_group = z_proxy_get_group(self->proxy); while (!self->connect_finished && z_proxy_group_iteration(proxy_group)) { ; } *conn = self->conn; res = TRUE; } } else { if (z_attach_setup_connector(self)) { ZStream *stream; if (z_connector_start_block(self->connector, &self->local, &stream)) { z_attach_callback(stream, NULL, self); *conn = self->conn; res = TRUE; } } } return res; } void z_attach_cancel(ZAttach *self) { if (self->connector) z_connector_cancel(self->connector); } /** * z_attach_new: * @proxy The proxy instance of the session * @bind_addr The address to bind to * @remote The address to connect to * @params The optional parameters for the connection * @callback Callback function to call when the connection is established * @notify Callback to call when the structure is destroyed * * Allocates and sets up a new instance of ZAttach. * (For the connection parameters see ZAttachTCPParams and ZAttachUDPParams.) * * Returns: * The new instance */ ZAttach * z_attach_new(ZProxy *proxy, guint proto, ZSockAddr *bind_addr, ZSockAddr *remote, ZAttachParams *params, ZAttachCallbackFunc callback, gpointer user_data, GDestroyNotify destroy_data) { ZAttach *self = g_new0(ZAttach, 1); gchar *session_id; session_id = proxy ? proxy->session_id : NULL; z_session_enter(session_id); g_strlcpy(self->session_id, session_id, sizeof(self->session_id)); if (proxy) self->proxy = z_proxy_ref(proxy); else self->proxy = NULL; self->proto = proto; self->bind_addr = z_sockaddr_ref(bind_addr); self->remote = z_sockaddr_ref(remote); self->callback = callback; self->user_data = user_data; self->destroy_data = destroy_data; memcpy(&self->params, params, sizeof(self->params)); z_session_leave(self->session_id); return self; } /** * z_attach_free: * @self this * * Free a ZAttach instance, * * Returns: * The instance */ void z_attach_free(ZAttach *self) { if (self) { if (self->user_data && self->destroy_data) { self->destroy_data(self->user_data); self->user_data = NULL; } if (self->proxy) z_proxy_unref(self->proxy); z_connector_unref(self->connector); z_sockaddr_unref(self->bind_addr); z_sockaddr_unref(self->local); z_sockaddr_unref(self->remote); g_free(self); } } zorp-3.9.5/lib/audit.c000066400000000000000000000023411172670260400145470ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi, Panther * Auditor : * Last audited version: * Notes: * ***************************************************************************/ zorp-3.9.5/lib/authprovider.c000066400000000000000000000051361172670260400161620ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include /** * z_auth_provider_check_passwd: * * NOTE: this function requires the Python lock to be held. **/ gboolean z_auth_provider_check_passwd( ZAuthProvider *self, gchar *session_id, gchar *username, gchar *passwd, gchar ***groups G_GNUC_UNUSED, ZProxy *proxy ) { gboolean called; ZPolicyObj *res; gboolean ret = FALSE; ZPolicyObj *session; z_session_enter(session_id); session = z_policy_getattr(proxy->handler, "session"); res = z_policy_call(self, "performAuthentication", z_policy_var_build("(sOss)", session_id, session, username, passwd), &called, session_id); z_policy_var_unref(session); if (res != NULL) { gboolean retval; if (z_policy_var_parse_boolean(res, &retval)) { z_log(session_id, CORE_INFO, 6, "Authentication backend called; username='%s', result='%d'", username, retval); ret = retval; } else { z_log(session_id, CORE_POLICY, 1, "Authentication backend returned a non-int type;"); } z_policy_var_unref(res); } z_session_leave(session_id); return ret; } zorp-3.9.5/lib/bllookup.c000066400000000000000000000000001172670260400152560ustar00rootroot00000000000000zorp-3.9.5/lib/connection.c000066400000000000000000000065471172670260400156140ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include /* * The ZConnection structure is used by ZAttach and ZDispatch to return * connection related information. It basically contains the address of the * peer the local bound address and the stream which can be used to * communicate with the peer. */ /* support functions for ZConnection */ /** * z_connection_new: * * Construct a new ZConnection instance and return a pointer to it. **/ ZConnection * z_connection_new(void) { return g_new0(ZConnection, 1); } /** * z_connection_format: * @conn: ZConnection instance * @buf: buffer to put output into * @buflen: size of @buf * * This function creates a textual representation of the ZConnection. It * puts its output into @buf ensuring that the output's size does not exceed * @buflen. * * Returns: the address of the first character in @buf **/ gchar * z_connection_format(ZConnection *conn, gchar *buf, gint buflen) { gchar buf_remote[MAX_SOCKADDR_STRING], buf_local[MAX_SOCKADDR_STRING], buf_dest[MAX_SOCKADDR_STRING]; if (!conn) { g_strlcpy(buf, "conn='NULL'", buflen); return buf; } if (conn->remote) z_sockaddr_format(conn->remote, buf_remote, sizeof(buf_remote)); else strcpy(buf_remote, "NULL"); if (conn->local) z_sockaddr_format(conn->local, buf_local, sizeof(buf_local)); else strcpy(buf_local, "NULL"); if (conn->dest) z_sockaddr_format(conn->dest, buf_dest, sizeof(buf_dest)); else strcpy(buf_dest, "NULL"); g_snprintf(buf, buflen, "protocol='%d', remote='%s', local='%s', dest='%s'", conn->protocol, buf_remote, buf_local, buf_dest); return buf; } /** * z_connection_destroy: * @conn: ZConnection instance * @close: specifies whether connection to the peer should be closed * * This function destructs and frees a ZConnection instance. **/ void z_connection_destroy(ZConnection *conn, gboolean close) { if (close) z_stream_close(conn->stream, NULL); z_sockaddr_unref(conn->remote); z_sockaddr_unref(conn->local); z_sockaddr_unref(conn->dest); z_dispatch_bind_unref(conn->dispatch_bind); z_stream_unref(conn->stream); g_free(conn); } zorp-3.9.5/lib/coredump.c000066400000000000000000000040131172670260400152550ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #ifdef HAVE_GOOGLE_COREDUMPER_H #include int z_coredump_create(void) { int res; GString *filename; filename = g_string_new("zorp_"); g_string_append_printf(filename, "%ld-%d.core", time(NULL), getpid()); /* Our $CWD is usually /var/run/zorp, we are supposed to be able to write there */ res = WriteCoreDump(filename->str); if (res == -1) { z_log(NULL, CORE_DEBUG, 3, "Failed to create core file; filename='%s', error='%s'", filename->str, g_strerror(errno)); } g_string_free(filename, TRUE); return res; } #else /* HAVE_GOOGLE_COREDUMPER_H */ int z_coredump_create(void) { z_log(NULL, CORE_DEBUG, 3, "Creating core dumps not supported;"); return -ENOTSUP; } #endif zorp-3.9.5/lib/credentials.c000066400000000000000000000000001172670260400157240ustar00rootroot00000000000000zorp-3.9.5/lib/dgram.c000066400000000000000000000453541172670260400145460ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Stream like operations for datagram based protocols. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include enum { ZDS_LISTEN = 1, ZDS_ESTABLISHED = 2, } ZorpDgramFlags; #ifndef IP_ORIGDSTADDR #define IP_ORIGDSTADDR 20 #endif #ifndef IP_RECVORIGDSTADDR #define IP_RECVORIGDSTADDR IP_ORIGDSTADDR #endif #ifndef IPV6_ORIGDSTADDR #define IPV6_ORIGDSTADDR 74 #endif #ifndef IPV6_RECVORIGDSTADDR #define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR #endif typedef struct _ZDgramSocketFuncs { gint (*open)(guint flags, ZSockAddr *remote, ZSockAddr *local, guint32 sock_flags, gint tos, GError **error); gboolean (*setup)(gint fd, guint flags, gint tos, gint family); GIOStatus (*recv)(gint fd, ZPktBuf **pack, ZSockAddr **from, ZSockAddr **to, gint *tos, gboolean peek, GError **error); } ZDgramSocketFuncs; /* Wrapper functions calling the underlying OS specific routines */ static ZDgramSocketFuncs *dgram_socket_funcs; /** * z_dgram_socket_open: * * Generic dgram_socket_open, will use the enabled one of _l22_, _nf_ or _ipf_. */ static gint z_dgram_socket_open(guint flags, ZSockAddr *remote, ZSockAddr *local, guint32 sock_flags, gint tos, GError **error) { return dgram_socket_funcs->open(flags, remote, local, sock_flags, tos, error); } /** * z_dgram_socket_setup: * * Generic dgram_socket_setup, will use the system dependent implementation _l22_ or _nf_. */ static gboolean z_dgram_socket_setup(gint fd, guint flags, gint tos, gint family) { return dgram_socket_funcs->setup(fd, flags, tos, family); } /** * z_dgram_socket_recv: * * Generic dgram_socket_recv, will use the enabled one of _l22_, _nf_ or _ipf_. */ GIOStatus z_dgram_socket_recv(gint fd, ZPktBuf **pack, ZSockAddr **from, ZSockAddr **to, gint *tos, gboolean peek, GError **error) { return dgram_socket_funcs->recv(fd, pack, from, to, tos, peek, error); } /** * z_nf_dgram_socket_open: * @flags: Additional flags: ZDS_LISTEN for incoming, ZDS_ESTABLISHED for outgoing socket * @remote: Address of the remote endpoint * @local: Address of the local endpoint * @sock_flags: Flags for binding, see 'z_bind' for details * @error: not used * * Create a new UDP socket - netfilter tproxy version * FIXME: some words about the difference * * Returns: * -1 on error, socket descriptor otherwise */ gint z_nf_dgram_socket_open(guint flags, ZSockAddr *remote, ZSockAddr *local, guint32 sock_flags, gint tos, GError **error G_GNUC_UNUSED) { gint fd; z_enter(); g_assert(local != NULL); fd = socket(z_map_pf(local->sa.sa_family), SOCK_DGRAM, 0); if (fd < 0) { /*LOG This message indicate that Zorp failed opening a new socket. It is likely that Zorp reached some resource limit. */ z_log(NULL, CORE_ERROR, 3, "Error opening socket; error='%s'", g_strerror(errno)); close(fd); z_return(-1); } if (!z_dgram_socket_setup(fd, flags, tos, local->sa.sa_family)) { /* z_dgram_socket_setup() already issued a log message */ close(fd); z_return(-1); } if (flags & ZDS_LISTEN) { if (z_bind(fd, local, sock_flags) != G_IO_STATUS_NORMAL) z_return(-1); /* z_bind already issued a log message */ } else if (flags & ZDS_ESTABLISHED) { struct sockaddr_in local_sa; socklen_t local_salen = sizeof(local_sa); if (z_bind(fd, local, sock_flags) != G_IO_STATUS_NORMAL) { close(fd); z_return(-1); } /* NOTE: we use connect instead of z_connect, as we do tproxy calls ourselves */ if (connect(fd, &remote->sa, remote->salen) < 0) { /*LOG This message indicates that UDP connection failed. */ z_log(NULL, CORE_ERROR, 3, "Error connecting UDP socket (nf); error='%s'", g_strerror(errno)); close(fd); z_return(-1); } /* get fully specified bind address (local might have a wildcard port number) */ if (getsockname(fd, (struct sockaddr *) &local_sa, &local_salen) < 0) { /*LOG This message indicates that Zorp was unable to query the local address. */ z_log(NULL, CORE_ERROR, 3, "Error querying local address (nf); error='%s'", g_strerror(errno)); close(fd); z_return(-1); } } /* flags & ZDS_ESTABLISHED */ z_return(fd); } /** * z_nf_dgram_socket_setup: * @fd: Socket descriptor to set up * @flags: Flags for binding, see 'z_bind' for details * * Returns: * FALSE if the setup operation failed, TRUE otherwise */ gboolean z_nf_dgram_socket_setup(gint fd, guint flags, gint tos, gint family) { gint tmp = 1; z_enter(); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)); tmp = 1; setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &tmp, sizeof(tmp)); if (flags & ZDS_LISTEN) { switch (family) { case PF_INET: tmp = 1; if (setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &tmp, sizeof(tmp)) < 0) { /*LOG This message indicates that the setsockopt requesting reception of original destination addresses of UDP frames failed. */ z_log(NULL, CORE_ERROR, 3, "Error during setsockopt(SOL_IP, IP_RECVORIGADDRS); error='%s'", g_strerror(errno)); z_return(FALSE); } #if ZORPLIB_ENABLE_TOS tmp = 1; if (setsockopt(fd, SOL_IP, IP_RECVTOS, &tmp, sizeof(tmp)) < 0) { z_log(NULL, CORE_ERROR, 3, "Error during setsockopt(SOL_IP, IP_RECVTOS); error='%s'", g_strerror(errno)); z_return(FALSE); } #endif break; case PF_INET6: tmp = 1; if (setsockopt(fd, SOL_IPV6, IPV6_RECVORIGDSTADDR, &tmp, sizeof(tmp)) < 0) { /*LOG This message indicates that the setsockopt requesting reception of original destination addresses of UDP frames failed. */ z_log(NULL, CORE_ERROR, 3, "Error during setsockopt(SOL_IPV6, IPV6_RECVORIGADDRS); error='%s'", g_strerror(errno)); /* FIXME: we should signal failure here, however, we must not do so because IPv6 tproxy support is not widespread enough to expect that it will be available. This also makes the unit tests fail. Should be removed once IPv6 tproxy support can be truly required for Zorp to function. Until that, even thoudh we try to set this socket option failing to do so is not a fatal error. */ } break; default: g_assert_not_reached(); } } else if (flags & ZDS_ESTABLISHED) { switch (family) { case PF_INET: z_fd_set_our_tos(fd, tos); break; } } z_return(TRUE); } /** * z_nf_dgram_socket_recv: * @fd: Socket descriptor to read from * @packet: The received packet * @from_addr: Address of the remote endpoint * @to_addr: Address of the local endpoint * @error: not used * * Receive data from an UDP socket and encapsulate it in a ZPktBuf. * Provides address information about the source and destination of * the packet. - netfilter tproxy version. * FIXME: some words about the difference * * Returns: * The status of the operation */ GIOStatus z_nf_dgram_socket_recv(gint fd, ZPktBuf **packet, ZSockAddr **from_addr, ZSockAddr **to_addr, gint *tos, gboolean peek, GError **error G_GNUC_UNUSED) { struct sockaddr from; gchar buf[65536], ctl_buf[64]; struct msghdr msg; struct cmsghdr *cmsg; struct iovec iov; gint rc; z_enter(); memset(&msg, 0, sizeof(msg)); msg.msg_name = &from; msg.msg_namelen = sizeof(from); msg.msg_controllen = sizeof(ctl_buf); msg.msg_control = ctl_buf; msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_base = buf; iov.iov_len = sizeof(buf); do { rc = recvmsg(fd, &msg, peek ? MSG_PEEK : 0); } while (rc < 0 && errno == EINTR); if (rc < 0) z_return(errno == EAGAIN ? G_IO_STATUS_AGAIN : G_IO_STATUS_ERROR); *packet = z_pktbuf_new(); z_pktbuf_copy(*packet, buf, rc); if (from_addr || to_addr || tos) { if (from_addr) *from_addr = NULL; if (to_addr) *to_addr = NULL; if (tos) *tos = -1; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg,cmsg)) { if (to_addr && cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_ORIGDSTADDR) { struct sockaddr_in *orig = (struct sockaddr_in *) CMSG_DATA(cmsg); if (orig->sin_addr.s_addr && orig->sin_port) { struct sockaddr_in to; to.sin_family = orig->sin_family; to.sin_addr = orig->sin_addr; to.sin_port = orig->sin_port; *to_addr = z_sockaddr_inet_new2(&to); } } else if (to_addr && cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_ORIGDSTADDR) { struct sockaddr_in6 *orig = (struct sockaddr_in6 *) CMSG_DATA(cmsg); if (!IN6_IS_ADDR_UNSPECIFIED(&orig->sin6_addr) && orig->sin6_port) { struct sockaddr_in6 to; to.sin6_family = orig->sin6_family; to.sin6_addr = orig->sin6_addr; to.sin6_port = orig->sin6_port; *to_addr = z_sockaddr_inet6_new2(&to); } } else if (tos && cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TOS) { memcpy(tos, CMSG_DATA(cmsg), sizeof(*tos)); } } if (to_addr && *to_addr == NULL) { struct sockaddr to; socklen_t tolen = sizeof(to); getsockname(fd, &to, &tolen); *to_addr = z_sockaddr_new(&to, tolen); } if (from_addr) { *from_addr = z_sockaddr_new(&from, sizeof(from)); } } z_return(G_IO_STATUS_NORMAL); } ZDgramSocketFuncs z_nf_dgram_socket_funcs = { z_nf_dgram_socket_open, z_nf_dgram_socket_setup, z_nf_dgram_socket_recv }; /** * z_dgram_init: * @sysdep_tproxy: Required functionality to use: Z_SD_TPROXY_[LINUX22|NETFILTER_V12|NETFILTER_V20] * * Module initialisation, initialises the function table according to the * requested transparency method. * * Returns: * TRUE on success */ gboolean z_dgram_init(void) { z_enter(); dgram_socket_funcs = &z_nf_dgram_socket_funcs; z_return(TRUE); } /* Datagram listener */ typedef struct _ZDGramListener { ZListener super; gint rcvbuf; gint session_limit; } ZDGramListener; ZClass ZDGramListener__class; static gint z_dgram_listener_open_listener(ZListener *s) { ZDGramListener *self = Z_CAST(s, ZDGramListener); gint fd; z_enter(); fd = z_dgram_socket_open(ZDS_LISTEN, NULL, s->bind_addr, s->sock_flags, -1, NULL); if (fd == -1) { /*LOG This message indicate that the creation of a new socket failed for the given reason. It is likely that the system is running low on memory, or the system is running out of the available fds. */ z_log(s->session_id, CORE_ERROR, 2, "Cannot create socket; error='%s'", g_strerror(errno)); z_return(-1); } z_fd_set_nonblock(fd, 1); if (self->rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &self->rcvbuf, sizeof(self->rcvbuf)) < 0) { z_log(s->session_id, CORE_ERROR, 2, "Cannot set receive buffer size on listening datagram socket; error='%s'", g_strerror(errno)); close(fd); z_return(-1); } if (z_getsockname(fd, &s->local, s->sock_flags) != G_IO_STATUS_NORMAL) { z_log(s->session_id, CORE_ERROR, 2, "Cannot query local address of listening datagram socket; error='%s'", g_strerror(errno)); close(fd); z_return(-1); } z_return(fd); } static GIOStatus z_dgram_listener_accept_connection(ZListener *self, ZStream **fdstream, ZSockAddr **client, ZSockAddr **dest) { gint newfd; GIOStatus res; ZSockAddr *from = NULL, *to = NULL; gint tos; ZPktBuf *packet; static gboolean udp_accept_available = TRUE; cap_t saved_caps; z_enter(); /* FIXME: using accept() on UDP sockets requires kernel extension */ if (udp_accept_available) { saved_caps = cap_save(); cap_enable(CAP_NET_ADMIN); cap_enable(CAP_NET_BIND_SERVICE); res = z_accept(self->fd, &newfd, client, self->sock_flags); if (res != G_IO_STATUS_NORMAL) { if (errno == EOPNOTSUPP) { cap_restore(saved_caps); udp_accept_available = FALSE; z_log(self->session_id, CORE_INFO, 4, "UDP accept() support unavailable, falling back to legacy datagram handling"); goto no_udp_accept; } else { if (errno != EAGAIN) z_log(self->session_id, CORE_ERROR, 1, "Error accepting on listening dgram socket; fd='%d', error='%s'", self->fd, g_strerror(errno)); cap_restore(saved_caps); z_return(res); } } cap_restore(saved_caps); /* this socket behaves like a listening one when we're reading the first packet to * determine the original destination address */ if (!z_dgram_socket_setup(newfd, ZDS_LISTEN, 0, self->local->sa.sa_family)) { close(newfd); z_return(G_IO_STATUS_ERROR); } /* we are not allowed to block on this operation, as due to a * race condition it's possible that accept() returns an fd * which has nothing in its queue */ z_fd_set_nonblock(newfd, 1); *dest = NULL; res = z_dgram_socket_recv(newfd, &packet, &from, &to, &tos, TRUE, NULL); if (res == G_IO_STATUS_AGAIN) { z_log(self->session_id, CORE_ERROR, 4, "No datagram messages are available in accepted socket; error='%s'", g_strerror(errno)); close(newfd); z_return(G_IO_STATUS_ERROR); } if (res != G_IO_STATUS_NORMAL) { z_log(self->session_id, CORE_ERROR, 3, "Error determining original destination address for datagram connection; error='%s'", g_strerror(errno)); res = G_IO_STATUS_NORMAL; } else { z_pktbuf_unref(packet); *dest = to; } z_fd_set_nonblock(newfd, 0); /* once we have the original address we set up the socket for establised mode; * this includes setting the TOS to the appropriate value */ if (!z_dgram_socket_setup(newfd, ZDS_ESTABLISHED, tos, self->local->sa.sa_family)) { res = G_IO_STATUS_ERROR; goto error_after_recv; } z_sockaddr_unref(from); *fdstream = z_stream_fd_new(newfd, ""); } else { no_udp_accept: *client = NULL; *dest = NULL; res = z_dgram_socket_recv(self->fd, &packet, &from, &to, &tos, FALSE, NULL); /* FIXME: fetch all packets in the receive buffer to be able to stuff * all to the newly created socket */ if (res == G_IO_STATUS_ERROR || from == NULL || to == NULL || packet == NULL) { z_log(self->session_id, CORE_ERROR, 1, "Error receiving datagram on listening stream; fd='%d', error='%s'", self->fd, g_strerror(errno)); } else { newfd = z_dgram_socket_open(ZDS_ESTABLISHED, from, to, ZSF_MARK_TPROXY, tos, NULL); if (newfd < 0) { z_log(self->session_id, CORE_ERROR, 3, "Error creating session socket, dropping packet; error='%s'", g_strerror(errno)); res = G_IO_STATUS_ERROR; } else { *fdstream = z_stream_fd_new(newfd, ""); if (*fdstream && !z_stream_unget_packet(*fdstream, packet, NULL)) { z_pktbuf_unref(packet); z_log(self->session_id, CORE_ERROR, 3, "Error creating session socket, dropping packet;"); close(newfd); } else { *client = z_sockaddr_ref(from); *dest = z_sockaddr_ref(to); } } z_sockaddr_unref(from); z_sockaddr_unref(to); } } z_return(res); error_after_recv: if (*dest != NULL) { z_sockaddr_unref(*dest); *dest = NULL; } z_sockaddr_unref(from); close(newfd); z_return(res); } ZListener * z_dgram_listener_new(const gchar *session_id, ZSockAddr *local, guint32 sock_flags, gint rcvbuf, ZAcceptFunc callback, gpointer user_data) { ZDGramListener *self; self = Z_CAST(z_listener_new(Z_CLASS(ZDGramListener), session_id, local, sock_flags, callback, user_data), ZDGramListener); if (self) { self->rcvbuf = rcvbuf; self->session_limit = 10; } return &self->super; } ZListenerFuncs z_dgram_listener_funcs = { { Z_FUNCS_COUNT(ZListener), NULL, }, z_dgram_listener_open_listener, z_dgram_listener_accept_connection }; Z_CLASS_DEF(ZDGramListener, ZListener, z_dgram_listener_funcs); /* datagram connector */ static ZConnectorFuncs z_dgram_connector_funcs = { { Z_FUNCS_COUNT(ZConnector), NULL, } }; ZClass ZDGramConnector__class = { Z_CLASS_HEADER, .super_class = Z_CLASS(ZConnector), .name = "ZDGramConnector", .size = sizeof(ZConnector), .funcs = &z_dgram_connector_funcs.super }; zorp-3.9.5/lib/dimhash.c000066400000000000000000000253161172670260400150650ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include /* * ZDimHashTable - Multi-dimensional hash table * * Similar to the linear hashes where the elements are (key, value) pairs, * but here the key is a vector, so the values occupy an N-dimensional space, * where N is the length of the key vector. * * Operations of this class include creating/destroying such tables (providing * the ability of using custom deallocator functions for values), * inserting into and deleting from the tables, searching for an exact match * of a key vector (it may even contain unspecified elements), and recursive * searching also. * */ /** * z_dim_hash_key_free: * @num Number of dimensions (==key parts) * @key Key vector * * Deallocates a key vector */ void z_dim_hash_key_free(int num, gchar **key) { int i; z_enter(); for (i = 0; i < num; i++) { if (key[i]) g_free(key[i]); } g_free(key); z_return(); } /** * z_dim_hash_nextstep: * @key Key to alter * @flags Flag that specify the required modification * * Helper for z_dim_hash_table_rec_search: * Alter key according to flags. If flags is DIMHASH_WILDCARD, then clear key, * if it's DIMHASH_CONSUME, then trim the last character only. * * Returns: Whether the operation was possible */ static gboolean z_dim_hash_nextstep(gchar *key, guint flags) { gboolean ret; z_enter(); if (!flags || (*key == 0)) z_return(FALSE); switch (flags) { case DIMHASH_WILDCARD: *key = 0; ret = TRUE; break; case DIMHASH_CONSUME: key[strlen(key)-1] = 0; ret = TRUE; break; default: ret = FALSE; break; } z_return(ret); } /** * z_dim_hash_table_makekey: * @new_key Destination of the composite key * @key_len Length of the destination buffer * @self Not used * @num Number of dimensions (key parts) * @key_parts Keys for dimensions, empty string or "*" means 'not specified' * * Creates a composite key from the dimensional key parts. The composite key * is a '::'-separated concatenation of the parts. * * Returns: TRUE on success, FALSE if the destination buffer is too short */ static gboolean z_dim_hash_table_makekey(gchar *new_key, guint key_len, ZDimHashTable *self G_GNUC_UNUSED, guint num, gchar **key_parts) { guint keylen; guint i; z_enter(); keylen = 0; for (i = 0; i < num; i++) keylen += strlen(key_parts[i]); memset(new_key, 0, key_len); if (keylen > key_len) z_return(FALSE); if (key_parts[0][0] != 0 && (key_parts[0][1] != 0 || key_parts[0][0] != '*')) strcpy(new_key, key_parts[0]); for (i = 1; i < num; i++) { strcat(new_key, "::"); if (key_parts[i][0] != 0 && (key_parts[i][1] != 0 || key_parts[i][0] != '*')) strcat(new_key, key_parts[i]); } z_return(TRUE); } /** * z_dim_hash_table_rec_search: * @self ZDimHashTable to search in * @num Number of specified keys * @num Depth of recursivity, the index of the currently processed key (internal) * @keys The current state of the key vector that's searched for * @save_keys The original key vector to search for * * Performs a recursive (depth) search for a key vector. * First it tries to find an exact match, then processes the last key according * to the flags of self (either DIMHASH_WILDCARD=wipe it at once or * DIMHASH_CONSUME=shrink it char-by-char), and tries to find a match. * If no match found, performs the same on the one-but-last and the last key, * and so on. * * Returns: NULL if no matching entry found, the first matching entry otherwise */ static gpointer * z_dim_hash_table_rec_search(ZDimHashTable *self, guint num, guint i, gchar **keys, gchar **save_keys) { gpointer *ret; gchar key[DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1]; guint keylen = DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1; z_enter(); if (i < num) { strcpy(keys[i], save_keys[i]); ret = z_dim_hash_table_rec_search(self, num, i + 1, keys, save_keys); while (!ret && z_dim_hash_nextstep(keys[i], self->flags[i])) ret = z_dim_hash_table_rec_search(self, num, i + 1, keys, save_keys); z_return(ret); } if (z_dim_hash_table_makekey(key, keylen, self, num, keys) && (key != NULL)) { ret = g_hash_table_lookup(self->hash, key); z_return(ret); } z_return(NULL); } /** * z_dim_hash_table_new: * @minnum Minimal number of specified dimensions for operations * @num Number of dimensions * @vararg Flags for the dimensions * * Create a new ZDimHashTable instance * The flags of the dimensions may be either DIMHASH_WILDCARD or DIMHASH_CONSUME, * specifying what modifications shall be performed on the key when doing a * recursive search (see z_dim_hash_table_rec_search). */ ZDimHashTable * z_dim_hash_table_new(guint minnum, guint num, ...) { guint i; va_list l; ZDimHashTable *self = g_new0(ZDimHashTable, 1); z_enter(); self->keynum = num; self->minkeynum = minnum; self->flags = g_new0(guint, num); va_start(l, num); for(i = 0; i < num; i++) self->flags[i] = va_arg(l, guint); va_end(l); self->hash = g_hash_table_new(g_str_hash, g_str_equal); z_return(self); } /** * z_dim_hash_table_free_item: * @key The composite key of the hash entry * @value The value of the hash entry * @user_data The function to use for freeing the value * * Helper for 'z_dim_hash_table_free', called during the hash traversal. * Frees a hash entry, using 'user_data' for deallocating the value and 'g_free' * for the key. * * Returns: Always TRUE -> requests the deletion of the entry */ static gboolean z_dim_hash_table_free_item(gpointer key, gpointer value, gpointer user_data) { gboolean (*fn)(void *) = user_data; z_enter(); fn(value); g_free(key); z_return(TRUE); } /** * z_dim_hash_table_free: * @self The ZDimHashTable instance to free * @func The function to use for freeing the values in the hash * * Iterates through the hash and frees all values using 'func' */ void z_dim_hash_table_free(ZDimHashTable *self, ZDimHashFreeFunc func) { z_enter(); if (func) g_hash_table_foreach_remove(self->hash, z_dim_hash_table_free_item, func); g_hash_table_destroy(self->hash); g_free(self->flags); g_free(self); z_return(); } /** * z_dim_hash_table_lookup: * @self The ZDimHashTable to search in * @num Number of dimensions (keys) specified * @keys The key vector to search for * * Search an entry of the hash that matches the specified key vector. * Empty strings and "*" are treated as non-specified elements. * * Returns: * NULL if too many or too few keys are specified or no matching entry found, * a pointer to the matching entry otherwise. */ gpointer z_dim_hash_table_lookup(ZDimHashTable *self, guint num, gchar **keys) { gchar key[DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1]; guint keylen = DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1; gpointer *ret = NULL; z_enter(); if (self->minkeynum > num || self->keynum < num) z_return(NULL); if (z_dim_hash_table_makekey(key, keylen, self, num, keys)) ret = g_hash_table_lookup(self->hash, key); z_return(ret); } /** * z_dim_hash_table_delete: * @self ZDimHashTable instance to delete from * @num Number of specified keys * @keys Key vector * @func The function to use for freeing entry values * * Delete the matching entry from the hash */ void z_dim_hash_table_delete(ZDimHashTable *self, guint num, gchar **keys, ZDimHashFreeFunc func) { gchar key[DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1]; guint keylen = DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1; gpointer orig_key; gpointer value; z_enter(); if (self->keynum < num || self->minkeynum > num) z_return(); if (z_dim_hash_table_makekey(key, keylen, self, num, keys) && g_hash_table_lookup_extended(self->hash, key, &orig_key, &value)) { g_hash_table_remove(self->hash, key); func(value); g_free(orig_key); } z_return(); } /** * z_dim_hash_table_insert: * @self ZDimHashTable instance to insert into * @value The value to insert * @num Number of specified keys * @keys Key vector * * Insert an entry into the hash */ void z_dim_hash_table_insert(ZDimHashTable *self, gpointer value, guint num, gchar **keys) { gchar key[DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1]; guint keylen = DIMHASH_MAX_KEYNUM * (DIMHASH_MAX_KEYSIZE + 2) + 1; gchar *new_key; z_enter(); if (self->keynum < num || self->minkeynum > num) z_return(); if (z_dim_hash_table_makekey(key, keylen, self, num, keys)) { new_key = g_strdup(key); g_hash_table_insert(self->hash, new_key, value); } z_return(); } /** * z_dim_hash_table_search: * @self ZDimHashTable instance to search in * @num Number of specified keys * @keys Key vector * * Searches self for an entry whose key matches the specified key vector * * Returns: NULL if error happened or no match found, or a pointer to the * matching entry */ gpointer z_dim_hash_table_search(ZDimHashTable *self, guint num, gchar **keys) { gchar *save_keys[DIMHASH_MAX_KEYNUM]; gpointer *ret = NULL; guint i; z_enter(); if (self->keynum < num || self->minkeynum > num) z_return(NULL); for (i = 0; i < num; i++) { save_keys[i] = alloca(DIMHASH_MAX_KEYSIZE); strncpy(save_keys[i], keys[i], DIMHASH_MAX_KEYSIZE - 1); save_keys[i][DIMHASH_MAX_KEYSIZE-1] = 0; } while (num > 0) { ret = z_dim_hash_table_rec_search(self, num, 0, save_keys, keys); if (ret) break; num--; } z_return(ret); } zorp-3.9.5/lib/dispatch.c000066400000000000000000001107101172670260400152400ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include /* * Dispatcher module * * The top of the dispatcher system is a hash table that consists of chains, * keyed by the (protocol, address) pair they are listening on. * Each such chain consists of elements, ordered by a priority value. * These elements contain callback functions that will be notified when a new * connection is established to the chain's input point. */ #define MAX_DISPATCH_BIND_STRING 128 /* our SockAddr based hash contains elements of this type */ typedef struct _ZDispatchChain { guint ref_cnt; gchar *session_id; ZDispatchBind *registered_key; ZSockAddr *bound_addr; GList *elements; GStaticRecMutex lock; gboolean threaded; GAsyncQueue *accept_queue; ZDispatchParams params; GList *listeners; GList *iface_watches; ZIfmonGroupWatch *iface_group_watch; } ZDispatchChain; /* Each ZDispatchChain structure contains a list of instances of this type */ struct _ZDispatchEntry { gchar *session_id; gint prio; ZDispatchBind *chain_key; ZDispatchCallbackFunc callback; gpointer callback_data; GDestroyNotify data_destroy; }; /* Global dispatch table and its mutex */ GHashTable *dispatch_table; GStaticMutex dispatch_lock = G_STATIC_MUTEX_INIT; /* * Locking within the Dispatch module * * There are two level locking within Dispatch: * 1) a global lock protecting the Dispatch hash table * 2) a per-chain lock protecting the chain's linked list and its reference counter * * If both locks are needed the global lock must be acquired first. */ typedef struct _ZListenerEntry { ZListener *listener; ZRefCount ref_cnt; } ZListenerEntry; /** * z_listener_entry_new: * @listener: stolen reference for a ZListener * * Creates a new ZListenerEntry object and borrow a reference for the listener * * Returns: the new listener entry */ ZListenerEntry * z_listener_entry_new(ZListener * listener) { ZListenerEntry *self = g_new0(ZListenerEntry, 1); g_assert(listener != NULL); self->listener = z_listener_ref(listener); z_refcount_set(&self->ref_cnt, 1); return self; } void z_listener_entry_destroy(ZListenerEntry *self) { z_listener_unref(self->listener); g_free(self); } ZListenerEntry * z_listener_entry_ref(ZListenerEntry *self) { z_refcount_inc(&self->ref_cnt); return self; } /** * z_listener_entry_unref: * @self: listener entry * * Decreases the reference count of the listener entry. If it reaches zero, the listener entry is freed * and the reference count listener inside of the object is decreased. * * Returns true if the entry is freed */ gboolean z_listener_entry_unref(ZListenerEntry *self) { if (self && z_refcount_dec(&self->ref_cnt)) { z_listener_entry_destroy(self); return TRUE; } return FALSE; } #define Z_DISPATCH_THREAD_EXIT_MAGIC ((ZConnection *) &z_dispatch_chain_thread) static gpointer z_dispatch_chain_thread(gpointer st); static void z_dispatch_connection(ZDispatchChain *chain, ZConnection *conn); /** * z_dispatch_bind_equal: * @key1 1st key * @key2 2nd key * * Compares the keys by their IP and port values, used for hash key checking * function (g_hash_table_new). * * Returns: * TRUE if they equal */ static gboolean z_dispatch_bind_equal(ZDispatchBind *key1, ZDispatchBind *key2) { if (key1->type != key2->type || key1->protocol != key2->protocol) return FALSE; switch (key1->type) { case ZD_BIND_SOCKADDR: return z_sockaddr_equal(key1->sa.addr, key2->sa.addr); case ZD_BIND_IFACE: return g_str_equal(key1->iface.iface, key2->iface.iface) && key1->iface.port == key2->iface.port && key1->iface.ip4.s_addr == key2->iface.ip4.s_addr; case ZD_BIND_IFACE_GROUP: return key1->iface_group.group == key2->iface_group.group && key1->iface_group.port == key2->iface_group.port; default: g_assert_not_reached(); } } /** * z_dispatch_bind_hash: * @key The key to generate a hash value from * * Generates an integer hash value from a key * * Returns: * The hash value */ static guint z_dispatch_bind_hash(ZDispatchBind *key) { switch (key->type) { case ZD_BIND_SOCKADDR: if (z_sockaddr_inet_check(key->sa.addr)) { struct sockaddr_in *s_in = (struct sockaddr_in *) &key->sa.addr->sa; return s_in->sin_family + ntohs(s_in->sin_port) + ntohl(s_in->sin_addr.s_addr) + key->protocol; } else if (z_sockaddr_inet6_check(key->sa.addr)) { struct sockaddr_in *s_in = (struct sockaddr_in *) &key->sa.addr->sa; return s_in->sin_family + ntohs(s_in->sin_port) + ntohl(s_in->sin_addr.s_addr) + key->protocol; } else g_assert_not_reached(); case ZD_BIND_IFACE: return g_str_hash(key->iface.iface) + ntohs(key->iface.port); case ZD_BIND_IFACE_GROUP: return (key->iface_group.group << 16) + ntohs(key->iface.port); default: g_assert_not_reached(); } } gchar * z_dispatch_bind_format(ZDispatchBind *self, gchar *buf, gsize buflen) { gchar sabuf[MAX_SOCKADDR_STRING]; gchar ipbuf[16]; switch (self->type) { case ZD_BIND_SOCKADDR: g_snprintf(buf, buflen, "SA(proto=%d,addr=%s)", self->protocol, z_sockaddr_format(self->sa.addr, sabuf, sizeof(sabuf))); break; case ZD_BIND_IFACE: /* FIXME: preferred IP should be formatted according to family */ g_snprintf(buf, buflen, "IFACE(proto=%d,iface=%s,ip=%s,port=%d,family=%d)", self->protocol, self->iface.iface, z_inet_ntoa(ipbuf, sizeof(ipbuf), self->iface.ip4), self->iface.port, self->iface.family); break; case ZD_BIND_IFACE_GROUP: g_snprintf(buf, buflen, "IFGROUP(proto=%d,iface_group=0x%x,port=%d,family=%d)", self->protocol, self->iface_group.group, self->iface_group.port, self->iface_group.family); break; default: g_assert_not_reached(); } return buf; } static gboolean z_dispatch_bind_is_wildcard(ZDispatchBind *self) { switch (self->type) { case ZD_BIND_SOCKADDR: if (z_sockaddr_inet_check(self->sa.addr) && z_sockaddr_inet_get_port(self->sa.addr) == 0) return TRUE; if (z_sockaddr_inet6_check(self->sa.addr) && z_sockaddr_inet6_get_port(self->sa.addr) == 0) return TRUE; break; case ZD_BIND_IFACE: return ntohs(self->iface.port) == 0; case ZD_BIND_IFACE_GROUP: return ntohs(self->iface_group.port) == 0; default: g_assert_not_reached(); } return FALSE; } static inline void z_dispatch_bind_init(ZDispatchBind *self, guint type, guint protocol) { z_refcount_set(&self->ref_cnt, 1); self->type = type; self->protocol = protocol; } /** * z_dispatch_bind_new_sa: * @addr: socket address * @protocol: protocol identifier (ZD_PROTO_*) * * Create a new ZDispatchBind instance initializing it based on @addr and * @protocol. ZDispatchBind instances are used for keying the Dispatch hash * table. * * Returns: new ZDispatchBind structure **/ ZDispatchBind * z_dispatch_bind_new_sa(guint protocol, ZSockAddr *addr) { ZDispatchBind *self = g_new0(ZDispatchBind, 1); z_dispatch_bind_init(self, ZD_BIND_SOCKADDR, protocol); self->sa.addr = z_sockaddr_ref(addr); return self; } /** * z_dispatch_bind_new_iface: * @iface: interface name * @protocol: protocol identifier (ZD_PROTO_*) * * Create a new ZDispatchBind instance initializing it based on @addr and * @protocol. ZDispatchBind instances are used for keying the Dispatch hash * table. * * Returns: new ZDispatchBind structure **/ ZDispatchBind * z_dispatch_bind_new_iface(guint protocol, const gchar *iface, gint family, const gchar *ip, guint port) { ZDispatchBind *self = g_new0(ZDispatchBind, 1); z_dispatch_bind_init(self, ZD_BIND_IFACE, protocol); g_strlcpy(self->iface.iface, iface, sizeof(self->iface.iface)); self->iface.family = family; self->iface.port = port; switch (family) { case AF_INET: if (z_inet_aton(ip, &self->iface.ip4)) break; // fallthrough in case of parse error default: z_log(NULL, CORE_ERROR, 1, "Error parsing bind ip of dispatcher; interface='%s', ip='%s'", iface, ip); g_assert_not_reached(); } return self; } /** * z_dispatch_bind_new_iface_group: * @group: interface name * @protocol: protocol identifier (ZD_PROTO_*) * * Create a new ZDispatchBind instance initializing it based on @group and * @protocol. ZDispatchBind instances are used for keying the Dispatch hash * table. * * Returns: new ZDispatchBind structure **/ ZDispatchBind * z_dispatch_bind_new_iface_group(guint protocol, guint32 group, gint family, guint port) { ZDispatchBind *self = g_new0(ZDispatchBind, 1); z_dispatch_bind_init(self, ZD_BIND_IFACE_GROUP, protocol); self->iface_group.group = group; self->iface_group.family = family; self->iface_group.port = port; return self; } /** * z_dispatch_bind_ref: * @self: this * * Add a reference to @self: **/ ZDispatchBind * z_dispatch_bind_ref(ZDispatchBind *self) { z_refcount_inc(&self->ref_cnt); return self; } /** * z_dispatch_bind_unref: * @self: this * * Decrement reference count for @self and free if that reaches zero. * **/ void z_dispatch_bind_unref(ZDispatchBind *self) { if (self && z_refcount_dec(&self->ref_cnt)) { if (self->type == ZD_BIND_SOCKADDR) z_sockaddr_unref(self->sa.addr); g_free(self); } } /** * z_dispatch_chain_lock: * @self this * * Lock the chain's mutex */ static inline void z_dispatch_chain_lock(ZDispatchChain *self) { g_static_rec_mutex_lock(&self->lock); } /** * z_dispatch_chain_unlock: * @self this * * Unlock the chain's mutex */ static inline void z_dispatch_chain_unlock(ZDispatchChain *self) { g_static_rec_mutex_unlock(&self->lock); } static inline ZDispatchChain *z_dispatch_chain_ref(ZDispatchChain *self); static inline void z_dispatch_chain_unref(ZDispatchChain *self); /** * z_dispatch_chain_thread: * @st this * * The thread routine of a dispatcher chain, pops new connections from the * accept_queue and processes them by calling z_dispatch_connection. * When the popped connection is the special value Z_DISPATCH_THREAD_EXIT_MAGIC, * exits the processing loop and finishes the thread. * * Returns: NULL */ static gpointer z_dispatch_chain_thread(gpointer st) { ZDispatchChain *self = (ZDispatchChain *) st; ZConnection *conn; glong acceptq_sum; gint count; /* g_thread_set_priority(g_thread_self(), G_THREAD_PRIORITY_HIGH); */ /*LOG This message reports that a new dispatcher thread is starting. This is used if threaded dispatching is enabled. @see: Dispatcher */ z_log(NULL, CORE_DEBUG, 4, "Dispatch thread starting;"); acceptq_sum = 0; count = 0; while (1) { acceptq_sum += g_async_queue_length(self->accept_queue); if (count % 1000 == 0) { /*LOG This message reports the dispatcher average accept queue length status. */ z_log(NULL, CORE_DEBUG, 4, "Accept queue stats; avg_length='%ld'", acceptq_sum / 1000); acceptq_sum = 0; } conn = g_async_queue_pop(self->accept_queue); if (conn == Z_DISPATCH_THREAD_EXIT_MAGIC) break; z_dispatch_connection(self, conn); count++; } /*LOG This message reports that the dispatcher thread is exiting. It it likely that Zorp unbinds from that address. */ z_log(NULL, CORE_DEBUG, 4, "Dispatch thread exiting;"); z_dispatch_chain_unref(self); return NULL; } /** * z_dispatch_chain_new: * @protocol Protocol identifier (ZD_PROTO_*) * @bind_addr Address to bind to * @params Additional parameters (see ZDispatch*Params) * * Constructor of ZDispatchChain, allocates and initialises a new instance, optionally * starts a processing thread for it. * * Returns: * The new instance */ static ZDispatchChain * z_dispatch_chain_new(const gchar *session_id, ZDispatchBind *key, ZDispatchParams *params) { ZDispatchChain *self = g_new0(ZDispatchChain, 1); gchar thread_name[256], buf[256]; z_enter(); self->session_id = strdup(session_id); self->ref_cnt = 1; self->registered_key = z_dispatch_bind_ref(key); self->threaded = ((ZDispatchCommonParams *) params)->threaded; memcpy(&self->params, params, sizeof(*params)); if (self->threaded) { self->accept_queue = g_async_queue_new(); z_dispatch_chain_ref(self); g_snprintf(thread_name, sizeof(thread_name), "dispatch(%s)", z_dispatch_bind_format(key, buf, sizeof(buf))); if (!z_thread_new(thread_name, z_dispatch_chain_thread, self)) { /*LOG This message indicates that Zorp was unable to create a dispatcher thread for accepting new connection, and it is reverting back to the original non-threaded mode. It is likely that Zorp reached its thread or resource limit. Check your logs for further information. */ z_log(NULL, CORE_ERROR, 2, "Error creating dispatch thread, falling back to non-threaded mode;"); z_dispatch_chain_unref(self); self->threaded = FALSE; g_async_queue_unref(self->accept_queue); self->accept_queue = NULL; } } z_return(self); } /** * z_dispatch_chain_ref: * @self this * * Increment the chain's reference counter. */ static inline ZDispatchChain * z_dispatch_chain_ref(ZDispatchChain *self) { z_dispatch_chain_lock(self); z_incref(&self->ref_cnt); z_dispatch_chain_unlock(self); return self; } /** * z_dispatch_chain_unref: * @self this * * Decrement the chain's reference counter, destroy it when the counter * reaches zero. */ static inline void z_dispatch_chain_unref(ZDispatchChain *self) { z_dispatch_chain_lock(self); if (z_decref(&self->ref_cnt) == 0) { z_dispatch_chain_unlock(self); if (self->accept_queue) g_async_queue_unref(self->accept_queue); z_dispatch_bind_unref(self->registered_key); z_sockaddr_unref(self->bound_addr); g_free(self->session_id); g_free(self); } else z_dispatch_chain_unlock(self); } /** * z_dispatch_entry_free: * @entry this * * Destructor of ZDispatchEntry */ static void z_dispatch_entry_free(ZDispatchEntry *entry) { g_free(entry->session_id); z_dispatch_bind_unref(entry->chain_key); if (entry->data_destroy) entry->data_destroy(entry->callback_data); g_free(entry); } /** * z_dispatch_entry_compare_prio: * @a 1st entry * @b 2nd entry * * Compares the two entries by their priority, used for ordered inserting * into the list by g_list_insert_sorted. * * Returns: * -1 if a is less prioritised than b * 0 if the priorities equals * 1 if a is more prioritised than b */ static gint z_dispatch_entry_compare_prio(ZDispatchEntry *a, ZDispatchEntry *b) { if (a->prio < b->prio) return -1; else if (a->prio == b->prio) return 0; else return 1; } /** * z_dispatch_connection: * @chain this * @conn The new connection to dispatch * * Iterates through the chain and dispatches the connection to the * chain items by passing it to their callbacks (for example to * z_py_zorp_dispatch_accept, which passes it to Dispatcher.accepted). */ static void z_dispatch_connection(ZDispatchChain *chain, ZConnection *conn) { GList *p; ZDispatchEntry *entry; gchar buf[256]; z_enter(); z_dispatch_chain_lock(chain); /* the list is ordered by priority */ for (p = chain->elements; p; p = g_list_next(p)) { entry = (ZDispatchEntry *) p->data; /*LOG This message reports that a new connections is coming. */ z_log(entry->session_id, CORE_DEBUG, 6, "Incoming connection; %s", conn ? z_connection_format(conn, buf, sizeof(buf)) : "conn=NULL"); if ((entry->callback)(conn, entry->callback_data)) { z_dispatch_chain_unlock(chain); z_return(); } } z_dispatch_chain_unlock(chain); /* nobody needed this connection, destroy it */ /*LOG This message indicates that a new connection was accepted, but no Listenet/Receiver/Proxy was interested in it. */ z_log(NULL, CORE_ERROR, 3, "Nobody was interested in this connection; %s", z_connection_format(conn, buf, sizeof(buf))); z_connection_destroy(conn, TRUE); z_return(); } /** * z_dispatch_accept: * @fdstream Socket stream * @client Address of remote endpoint * @dest Address of original destination * @user_data this * * Internal callback, called when a new incoming connection is established. * Creates and initialises a new ZConnection, and dispatches it to the chain * either synchronously by z_dispatch_connection or asynchronously by pushing * it to the chain's accept queue. * * Note: this function runs in the main thread. * * Returns: TRUE */ static gboolean z_dispatch_accept(ZStream *fdstream, ZSockAddr *client, ZSockAddr *dest, gpointer user_data) { ZConnection *conn = NULL; ZDispatchChain *chain = (ZDispatchChain *) user_data; z_enter(); if (fdstream == NULL) { z_dispatch_connection(chain, NULL); z_return(TRUE); } if (chain->params.common.transparent) { ZSockAddr *listen_addr = NULL; gboolean non_transparent = FALSE; GList *p; switch (chain->registered_key->type) { case ZD_BIND_SOCKADDR: listen_addr = chain->registered_key->sa.addr; non_transparent = z_sockaddr_equal(listen_addr, dest); break; case ZD_BIND_IFACE: case ZD_BIND_IFACE_GROUP: /* NOTE: we are running in the main thread just like the * code that manipulates chain->listeners, thus we don't need to * lock here. This is even true for threaded listeners as * z_dispatch_accept runs in the main thread in that case too. */ for (p = chain->listeners; p; p = p->next) { ZListener *l = ((ZListenerEntry *) p->data)->listener; if (z_sockaddr_equal(l->local, dest)) { non_transparent = TRUE; listen_addr = l->local; break; } } break; } if (non_transparent) { gchar buf1[MAX_SOCKADDR_STRING], buf2[MAX_SOCKADDR_STRING]; /*LOG This message indicates that Listener/Receiver was configured to be accept transparent connections, but it was connected directly. Configure it either non-transparent or deny direct access to it and set up the appropriate TPROXY rule. @see: Listener @see: Receiver */ z_log(chain->session_id, CORE_ERROR, 1, "Transparent listener connected directly, dropping connection; local='%s', client_local='%s'", z_sockaddr_format(listen_addr, buf1, sizeof(buf1)), z_sockaddr_format(dest, buf2, sizeof(buf2))); z_stream_close(fdstream, NULL); z_stream_unref(fdstream); z_sockaddr_unref(client); z_sockaddr_unref(dest); z_return(TRUE); } } conn = z_connection_new(); conn->remote = client; conn->dest = dest; conn->local = z_sockaddr_ref(conn->dest); conn->dispatch_bind = z_dispatch_bind_ref(chain->registered_key); conn->protocol = chain->registered_key->protocol; conn->stream = fdstream; if (chain->threaded) g_async_queue_push(chain->accept_queue, conn); else z_dispatch_connection(chain, conn); z_return(TRUE); } static ZListener * z_dispatch_new_listener(ZDispatchChain *chain, ZSockAddr *local) { ZListener *listener = NULL; guint32 sock_flags = (chain->params.common.mark_tproxy ? ZSF_MARK_TPROXY : 0) | (chain->params.common.transparent ? ZSF_TRANSPARENT : 0); if (chain->registered_key->protocol == ZD_PROTO_TCP) { sock_flags |= chain->params.tcp.accept_one ? ZSF_ACCEPT_ONE : 0; listener = z_stream_listener_new(chain->session_id, local, sock_flags, chain->params.tcp.backlog, z_dispatch_accept, chain); } else if (chain->registered_key->protocol == ZD_PROTO_UDP) { listener = z_dgram_listener_new(chain->session_id, local, sock_flags, chain->params.udp.rcvbuf, z_dispatch_accept, chain); } return listener; } static gboolean z_dispatch_iface_addr_matches(gint family, void *addr, ZDispatchBind *db) { gboolean match = FALSE; if (db->type == ZD_BIND_IFACE) { switch (family) { case AF_INET: match = (db->iface.ip4.s_addr == 0) || (memcmp(addr, &db->iface.ip4, sizeof(struct in_addr)) == 0); break; default: g_assert_not_reached(); break; } } else if (db->type == ZD_BIND_IFACE_GROUP) { match = TRUE; } return match; } static ZSockAddr * z_dispatch_iface_to_sa(gint family, void *addr, guint16 port) { gchar buf[16]; switch (family) { case AF_INET: z_inet_ntoa(buf, sizeof(buf), *((struct in_addr *) addr)); return z_sockaddr_inet_new(buf, port); default: g_assert_not_reached(); break; } return NULL; } /** * z_dispatch_bind_iface_change: * * NOTE: this runs in the main thread **/ static void z_dispatch_bind_iface_change(const gchar *iface G_GNUC_UNUSED, ZIfChangeType change, gint family, void *addr, gpointer user_data) { ZDispatchChain *chain = (ZDispatchChain *) user_data; ZListener *listener; ZListenerEntry *listener_entry; gchar buf1[MAX_DISPATCH_BIND_STRING]; gchar buf2[MAX_SOCKADDR_STRING]; ZSockAddr *sa = NULL; gushort port; z_dispatch_bind_format(chain->registered_key, buf1, sizeof(buf1)); if (chain->registered_key->type == ZD_BIND_IFACE) port = chain->registered_key->iface.port; else if (chain->registered_key->type == ZD_BIND_IFACE_GROUP) port = chain->registered_key->iface_group.port; else g_assert_not_reached(); switch (change) { case Z_IFC_ADD: sa = z_dispatch_iface_to_sa(family, addr, port); z_sockaddr_format(sa, buf2, sizeof(buf2)); if (z_dispatch_iface_addr_matches(family, addr, chain->registered_key)) { GList *p; gboolean listener_exists = FALSE; for (p = chain->listeners; p; p = p->next) { listener_entry = (ZListenerEntry *) p->data; if (z_sockaddr_equal(sa, listener_entry->listener->bind_addr)) { listener_exists = TRUE; z_listener_entry_ref(listener_entry); } } if (!listener_exists) { z_log(chain->session_id, CORE_DEBUG, 4, "Adding dynamic interface address; addr='%s', dispatch='%s'", buf2, buf1); listener = z_dispatch_new_listener(chain, sa); if (listener) { listener_entry = z_listener_entry_new(listener); chain->listeners = g_list_prepend(chain->listeners, listener_entry); if (!z_listener_start(listener)) { chain->listeners = g_list_remove(chain->listeners, listener_entry); z_listener_entry_unref(listener_entry); } z_listener_unref(listener); } } else { z_log(chain->session_id, CORE_DEBUG, 5, "Dynamic interface address already bound, skipping bind this time; addr='%s', dispatch='%s'", buf2, buf1); } } else { z_log(chain->session_id, CORE_DEBUG, 5, "Address does not match expected dynamic address; addr='%s', dispatch='%s'", buf2, buf1); } z_sockaddr_unref(sa); break; case Z_IFC_REMOVE: { GList *p, *p_next; sa = z_dispatch_iface_to_sa(family, addr, port); z_sockaddr_format(sa, buf2, sizeof(buf2)); z_log(chain->session_id, CORE_DEBUG, 4, "Removing dynamic interface address; addr='%s', dispatch='%s'", buf2, buf1); for (p = chain->listeners; p; p = p_next) { p_next = g_list_next(p); listener_entry = (ZListenerEntry *) p->data; if (z_sockaddr_equal(sa, listener_entry->listener->bind_addr)) { ZListener *l = z_listener_ref(listener_entry->listener); if (z_listener_entry_unref(listener_entry)) { z_listener_cancel(l); chain->listeners = g_list_delete_link(chain->listeners, p); } z_listener_unref(l); } } z_sockaddr_unref(sa); } break; } } /** * z_dispatch_bind_iface_change: * * NOTE: this runs in the main thread **/ static void z_dispatch_bind_iface_group_change(guint32 group, ZIfChangeType change, const gchar *if_name, gpointer user_data) { ZDispatchChain *chain = (ZDispatchChain *) user_data; switch (change) { case Z_IFC_ADD: chain->iface_watches = g_list_prepend(chain->iface_watches, z_ifmon_register_watch(if_name, chain->registered_key->iface_group.family, z_dispatch_bind_iface_change, chain, NULL)); z_log(chain->session_id, CORE_DEBUG, 4, "Interface added to group; group='0x%x', name='%s'", group, if_name); break; case Z_IFC_REMOVE: { GList *p, *p_next; ZIfmonWatch *watch; for (p = chain->iface_watches; p; p = p_next) { p_next = g_list_next(p); watch = (ZIfmonWatch *) p->data; if (z_ifmon_watch_iface_matches(watch, if_name)) { z_ifmon_unregister_watch(watch); chain->iface_watches = g_list_delete_link(chain->iface_watches, p); break; } } z_log(chain->session_id, CORE_DEBUG, 4, "Interface removed from group; group='0x%x', name='%s'", group, if_name); } break; } } /** * z_dispatch_bind_listener: * @session_id Session identifier * @chain this * * Starts listening/receiving for a chain. * * Returns: * TRUE on success */ static gboolean z_dispatch_bind_listener(ZDispatchChain *chain, ZDispatchBind **bound_key) { gboolean rc = TRUE; ZListener *listener; z_enter(); *bound_key = NULL; switch (chain->registered_key->type) { case ZD_BIND_SOCKADDR: listener = z_dispatch_new_listener(chain, chain->registered_key->sa.addr); if (listener) { ZListenerEntry *entry = z_listener_entry_new(listener); z_listener_unref(listener); chain->listeners = g_list_prepend(chain->listeners, entry); /* open fd so that we can get the local address */ if (!z_listener_open(listener)) { chain->listeners = g_list_remove(chain->listeners, entry); z_listener_entry_unref(entry); rc = FALSE; break; } chain->bound_addr = z_sockaddr_ref(listener->local); if (!z_listener_start(listener)) { chain->bound_addr = NULL; z_sockaddr_unref(listener->local); chain->listeners = g_list_remove(chain->listeners, entry); z_listener_entry_unref(entry); rc = FALSE; break; } *bound_key = z_dispatch_bind_new_sa(chain->registered_key->protocol, chain->bound_addr); } else rc = FALSE; break; case ZD_BIND_IFACE: if (chain->registered_key->protocol == 0) return FALSE; /* NOTE: we don't add a reference to chain, this interface * registration is always deleted before this ZDispatchChain * instance would be */ chain->iface_watches = g_list_prepend(chain->iface_watches, z_ifmon_register_watch(chain->registered_key->iface.iface, chain->registered_key->iface.family, z_dispatch_bind_iface_change, chain, NULL)); *bound_key = z_dispatch_bind_ref(chain->registered_key); break; case ZD_BIND_IFACE_GROUP: if (chain->registered_key->protocol == 0) return FALSE; /* NOTE: we don't add a reference to chain, this interface * registration is always deleted before this ZDispatchChain * instance would be */ chain->iface_group_watch = z_ifmon_register_group_watch(chain->registered_key->iface_group.group, z_dispatch_bind_iface_group_change, chain, NULL); *bound_key = z_dispatch_bind_ref(chain->registered_key); break; } z_leave(); return rc; } /** * z_dispatch_unbind_listener: * @chain this * * Stops listening/receiving for a chain. If the chain was using a processing\ * thread, notifies it by sending the special connection value Z_DISPATCH_THREAD_EXIT_MAGIC * to it. */ static void z_dispatch_unbind_listener(ZDispatchChain *chain) { GList *p; z_enter(); if (chain->threaded) { /* send exit magic to our threads */ g_async_queue_push(chain->accept_queue, Z_DISPATCH_THREAD_EXIT_MAGIC); } if (chain->iface_group_watch) z_ifmon_unregister_group_watch(chain->iface_group_watch); while (chain->iface_watches) { z_ifmon_unregister_watch((ZIfmonWatch *) chain->iface_watches->data); chain->iface_watches = g_list_delete_link(chain->iface_watches, chain->iface_watches); } for (p = chain->listeners; p; p = g_list_next(p)) { ZListenerEntry *l = (ZListenerEntry *) p->data; z_listener_cancel(l->listener); /* FIXME: refence counting??? */ z_listener_entry_destroy(l); } g_list_free(chain->listeners); chain->listeners = NULL; z_return(); } /** * z_dispatch_register: * @session_id Session identifier * @key: ZDispatchBind instance * @bound_addr The address actually bound to * @prio Priority level * @params Additional parameters, see ZDispatch*Params * @cb Callback to call when a connection is established * @user_data this * @data_destroy Pointer to the destructor * * Constructor for ZDispatchEntry. Creates and initialises a new chain item * instance, looks for a chain that is bound to the requested address, creates * a new one if no such chain found, inserts it into the chain ordered by * priority. * * Returns: * The new instance */ ZDispatchEntry * z_dispatch_register(gchar *session_id, ZDispatchBind *key, ZSockAddr **bound_addr, gint prio, ZDispatchParams *params, ZDispatchCallbackFunc cb, gpointer user_data, GDestroyNotify data_destroy) { ZDispatchChain *chain; ZDispatchEntry *entry = NULL; ZDispatchBind *bound_key; z_session_enter(session_id); g_static_mutex_lock(&dispatch_lock); if (z_dispatch_bind_is_wildcard(key)) chain = NULL; else chain = g_hash_table_lookup(dispatch_table, key); if (!chain) { /* create hash chain */ chain = z_dispatch_chain_new(session_id, key, params); if (!z_dispatch_bind_listener(chain, &bound_key)) { z_dispatch_chain_unref(chain); g_static_mutex_unlock(&dispatch_lock); z_session_leave(session_id); return NULL; } /* chain is stored for the bound key (e.g. without the wildcard port) */ g_hash_table_insert(dispatch_table, z_dispatch_bind_ref(bound_key), chain); } else { if (key->protocol == ZD_PROTO_TCP && chain->params.tcp.accept_one) { gchar buf[MAX_DISPATCH_BIND_STRING]; /*LOG This message indicates that a Listener/Receiver/Proxy was unable bind to a specified address, because another instance is already listening there and specified that only one connection could be accepted. */ z_log(session_id, CORE_ERROR, 1, "Error registering dispatch, previous entry specified accept_one; dispatch='%s'", z_dispatch_bind_format(key, buf, sizeof(buf))); goto error; } /* we have a fully specified key, which was already registered, bound_key == key */ bound_key = z_dispatch_bind_ref(key); z_dispatch_chain_ref(chain); } if (bound_addr) *bound_addr = z_sockaddr_ref(chain->bound_addr); entry = g_new0(ZDispatchEntry, 1); entry->chain_key = bound_key; entry->session_id = g_strdup(session_id); entry->prio = prio; entry->callback = cb; entry->callback_data = user_data; entry->data_destroy = data_destroy; z_dispatch_chain_lock(chain); chain->elements = g_list_insert_sorted(chain->elements, entry, (GCompareFunc) z_dispatch_entry_compare_prio); z_dispatch_chain_unlock(chain); error: g_static_mutex_unlock(&dispatch_lock); z_session_leave(session_id); return entry; } /** * z_dispatch_unregister: * @entry this * * Destructor of ZDispatchEntry. Removes the entry from its chain, * destroying the chain if this was the last entry in it. */ void z_dispatch_unregister(ZDispatchEntry *entry) { ZDispatchChain *chain; ZDispatchBind *key; gchar buf[MAX_DISPATCH_BIND_STRING]; gboolean found, unbind; gpointer orig_key, orig_chain; z_enter(); g_static_mutex_lock(&dispatch_lock); found = g_hash_table_lookup_extended(dispatch_table, entry->chain_key, &orig_key, &orig_chain); key = (ZDispatchBind *) orig_key; chain = (ZDispatchChain *) orig_chain; if (found && chain) { GList *p; z_dispatch_chain_lock(chain); p = g_list_find(chain->elements, entry); if (p) { chain->elements = g_list_delete_link(chain->elements, p); z_dispatch_entry_free(entry); } else { /*LOG This message indicates that a Listener/Receiver/Proxy tries to unbind from the specified address, but have not registered itself to that address. */ z_log(NULL, CORE_ERROR, 1, "Internal error, dispatch entry not found (chain exists); dispatch='%s', entry='%p'", z_dispatch_bind_format(entry->chain_key, buf, sizeof(buf)), entry); } g_assert(chain->ref_cnt >= (guint) (1 + (guint) (!!chain->threaded))); unbind = chain->ref_cnt == (guint) (1 + (guint) (!!chain->threaded)); z_dispatch_chain_unlock(chain); if (unbind) { /* we need to unlock first as the underlying listener has its own * lock which is locked in the reverse order when the callback is * called. */ z_dispatch_unbind_listener(chain); if (!g_hash_table_remove(dispatch_table, key)) g_assert_not_reached(); z_dispatch_bind_unref(key); } z_dispatch_chain_unref(chain); } else { /*LOG This message indicates that a Listener/Receiver/Proxy tries to unbind from the specified address, but Zorp does not bind to that address. */ z_log(NULL, CORE_ERROR, 1, "Internal error, dispatch entry not found (no chain); dispatch='%s', entry='%p'", z_dispatch_bind_format(entry->chain_key, buf, sizeof(buf)), entry); } g_static_mutex_unlock(&dispatch_lock); z_return(); } /* module initialization */ /** * z_dispatch_init: * * Initialises the global hash table of chains. */ void z_dispatch_init(void) { dispatch_table = g_hash_table_new((GHashFunc) z_dispatch_bind_hash, (GEqualFunc) z_dispatch_bind_equal); } /** * z_dispatch_destroy: * * Destroys the global hash table of chains. * FIXME?: what happens if there are still chains in the table? */ void z_dispatch_destroy(void) { if (dispatch_table) { g_hash_table_destroy(dispatch_table); dispatch_table = NULL; } } zorp-3.9.5/lib/idsconn.c000066400000000000000000000000001172670260400150640ustar00rootroot00000000000000zorp-3.9.5/lib/ifmonitor.c000066400000000000000000000502611172670260400154530ustar00rootroot00000000000000#include #include #include #include #include #include #include #include typedef void (*ZNetlinkEventHandlerFunc)(const gchar *msg, gsize msg_len); typedef struct _ZNetlinkEventHandler { guint16 event; ZNetlinkEventHandlerFunc callback; } ZNetlinkEventHandler; static GSource *netlink_source; static gint netlink_fd; static guint32 netlink_seq; static GList *netlink_event_handlers; static gboolean netlink_initialized = FALSE; /* rtnetlink requests */ gboolean z_rtnetlink_request_dump(gint type, gint family) { struct { struct nlmsghdr h; struct rtgenmsg g; } nlreq; struct sockaddr_nl nladdr; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; memset(&nlreq, 0, sizeof(nlreq)); nlreq.h.nlmsg_len = sizeof(nlreq); nlreq.h.nlmsg_type = type; nlreq.h.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT | NLM_F_DUMP; nlreq.h.nlmsg_seq = netlink_seq++; nlreq.h.nlmsg_pid = getpid(); nlreq.g.rtgen_family = family; if (sendto(netlink_fd, (void *) &nlreq, sizeof(nlreq), 0, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) return FALSE; return TRUE; } /* generic netlink code */ /* not safe to call from parallel threads */ gboolean z_netlink_register(gint event, ZNetlinkEventHandlerFunc callback) { ZNetlinkEventHandler *h; h = g_new0(ZNetlinkEventHandler, 1); h->event = event; h->callback = callback; netlink_event_handlers = g_list_prepend(netlink_event_handlers, h); return TRUE; } static gboolean z_netlink_process_responses(gboolean timed_out G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { gchar data[4096]; gssize data_len; gsize len; struct nlmsghdr *h; GList *p; data_len = recv(netlink_fd, data, sizeof(data), 0); if (data_len < 0) { z_log(NULL, CORE_ERROR, 1, "Error receiving netlink message; error='%s'", g_strerror(errno)); return FALSE; } h = ((struct nlmsghdr *) &data); len = data_len; while (NLMSG_OK(h, len)) { for (p = netlink_event_handlers; p; p = g_list_next(p)) { ZNetlinkEventHandler *handler = (ZNetlinkEventHandler *) p->data; if (handler->event == h->nlmsg_type) { handler->callback((gchar *) h, h->nlmsg_len); } } h = NLMSG_NEXT(h, len); } return TRUE; } void z_netlink_init(void) { struct sockaddr_nl nladdr; netlink_seq = time(NULL); netlink_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); if (netlink_fd < 0) { z_log(NULL, CORE_ERROR, 1, "Error opening netlink socket, interface information will not be available; error='%s'", g_strerror(errno)); return; } memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; /* FIXME: the exact groups we want to be notified about might change depending on the users of this netlink interface */ nladdr.nl_groups = RTMGRP_IPV4_IFADDR|RTMGRP_LINK; if (bind(netlink_fd, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { z_log(NULL, CORE_ERROR, 1, "Error binding netlink socket, interface information will not be available; error='%s'", g_strerror(errno)); close(netlink_fd); return; } netlink_source = z_socket_source_new(netlink_fd, G_IO_IN, -1); g_source_set_callback(netlink_source, (GSourceFunc) z_netlink_process_responses, NULL, NULL); g_source_attach(netlink_source, NULL); netlink_initialized = TRUE; } void z_netlink_destroy(void) { if (netlink_initialized) { g_source_destroy(netlink_source); g_source_unref(netlink_source); netlink_source = NULL; close(netlink_fd); netlink_initialized = FALSE; } /* FIXME: free netlink_event_handlers */ } /* interface address monitoring */ #define Z_IFNAME_MAX IF_NAMESIZE #define Z_IFADDR_MAX 256 typedef struct ZIfaceInfo { guint index; gchar name[Z_IFNAME_MAX]; guint32 group; guint32 flags; guint16 in4_address_count; struct in_addr in4_addresses[Z_IFADDR_MAX]; } ZIfaceInfo; struct _ZIfmonWatch { gchar iface_name[Z_IFNAME_MAX]; gint family; ZIfmonWatchFunc callback; gpointer user_data; GDestroyNotify user_data_destroy; }; struct _ZIfmonGroupWatch { guint32 group; ZIfmonGroupWatchFunc callback; gpointer user_data; GDestroyNotify user_data_destroy; }; static GHashTable *iface_hash; static GList *iface_watches; static GList *iface_group_watches; static GStaticMutex iface_watches_lock; static GStaticMutex iface_group_watches_lock; static gboolean match_by_name(guint *ifindex G_GNUC_UNUSED, ZIfaceInfo *info, gchar *iface_name) { return strcmp(info->name, iface_name) == 0; } static ZIfaceInfo * z_ifmon_find_by_name(const gchar *iface_name) { return (ZIfaceInfo *) g_hash_table_find(iface_hash, (GHRFunc) match_by_name, (gchar *) iface_name); } static void z_ifmon_call_watchers_unlocked(const gchar *iface, ZIfChangeType change, gint family, void *addr) { GList *p, *p_next; for (p = iface_watches; p; p = p_next) { ZIfmonWatch *w = (ZIfmonWatch *) p->data; p_next = g_list_next(p); if (strcmp(w->iface_name, iface) == 0 && (!family || w->family == family)) { w->callback(w->iface_name, change, family, addr, w->user_data); } } } static void z_ifmon_call_watchers(const gchar *iface, ZIfChangeType change, gint family, void *addr) { g_static_mutex_lock(&iface_watches_lock); z_ifmon_call_watchers_unlocked(iface, change, family, addr); g_static_mutex_unlock(&iface_watches_lock); } static void z_ifmon_iterate_addrs(ZIfaceInfo *info, ZIfChangeType change) { gint i; if (!info) return; g_static_mutex_lock(&iface_watches_lock); for (i = 0; i < info->in4_address_count; i++) { z_ifmon_call_watchers_unlocked(info->name, change, AF_INET, &info->in4_addresses[i]); } g_static_mutex_unlock(&iface_watches_lock); } gboolean z_ifmon_watch_iface_matches(ZIfmonWatch *w, const gchar *if_name) { return strcmp(w->iface_name, if_name) == 0; } static inline void z_ifmon_watch_free(ZIfmonWatch *watch) { if (watch->user_data_destroy) watch->user_data_destroy(watch->user_data); g_free(watch); } ZIfmonWatch * z_ifmon_register_watch(const gchar *iface, gint family, ZIfmonWatchFunc callback, gpointer user_data, GDestroyNotify user_data_destroy) { ZIfmonWatch *w = g_new0(ZIfmonWatch, 1); ZIfaceInfo *info; g_strlcpy(w->iface_name, iface, sizeof(w->iface_name)); w->callback = callback; w->family = family; w->user_data = user_data; w->user_data_destroy = user_data_destroy; /* add all already registered addresses */ info = z_ifmon_find_by_name(iface); if (info && info->flags & IFF_UP) { gint i; for (i = 0; i < info->in4_address_count; i++) { callback(iface, Z_IFC_ADD, AF_INET, &info->in4_addresses[i], user_data); } } g_static_mutex_lock(&iface_watches_lock); iface_watches = g_list_prepend(iface_watches, w); g_static_mutex_unlock(&iface_watches_lock); return w; } void z_ifmon_unregister_watch(ZIfmonWatch *watch) { ZIfaceInfo *info; info = z_ifmon_find_by_name(watch->iface_name); if (info && info->flags & IFF_UP) { gint i; for (i = 0; i < info->in4_address_count; i++) { watch->callback(watch->iface_name, Z_IFC_REMOVE, AF_INET, &info->in4_addresses[i], watch->user_data); } } g_static_mutex_lock(&iface_watches_lock); iface_watches = g_list_remove(iface_watches, watch); g_static_mutex_unlock(&iface_watches_lock); z_ifmon_watch_free(watch); } typedef struct _ZIfmonGroupIterState { guint32 group; ZIfChangeType change; ZIfmonGroupWatch *watch; } ZIfmonGroupIterState; static void z_ifmon_iterate_by_group(guint *ifindex G_GNUC_UNUSED, ZIfaceInfo *info, ZIfmonGroupIterState *state) { if (info->group == state->group) { state->watch->callback(info->group, state->change, info->name, state->watch->user_data); } } static void z_ifmon_call_group_watchers_unlocked(guint32 group, ZIfChangeType change, const gchar *if_name) { GList *p, *p_next; for (p = iface_group_watches; p; p = p_next) { ZIfmonGroupWatch *w = (ZIfmonGroupWatch *) p->data; p_next = g_list_next(p); if (w->group == group) { w->callback(group, change, if_name, w->user_data); } } } static void z_ifmon_call_group_watchers(guint32 group, ZIfChangeType change, const gchar *if_name) { g_static_mutex_lock(&iface_group_watches_lock); z_ifmon_call_group_watchers_unlocked(group, change, if_name); g_static_mutex_unlock(&iface_group_watches_lock); } static void z_ifmon_iterate_ifaces(guint32 group, ZIfmonGroupWatch *watch, ZIfChangeType change) { ZIfmonGroupIterState state; state.change = change; state.group = group; state.watch = watch; g_hash_table_foreach(iface_hash, (GHFunc) z_ifmon_iterate_by_group, &state); } static inline void z_ifmon_group_watch_free(ZIfmonGroupWatch *watch) { if (watch->user_data_destroy) watch->user_data_destroy(watch->user_data); g_free(watch); } ZIfmonGroupWatch * z_ifmon_register_group_watch(guint32 group, ZIfmonGroupWatchFunc callback, gpointer user_data, GDestroyNotify user_data_destroy) { ZIfmonGroupWatch *w = g_new0(ZIfmonGroupWatch, 1); w->group = group; w->callback = callback; w->user_data = user_data; w->user_data_destroy = user_data_destroy; g_static_mutex_lock(&iface_group_watches_lock); iface_group_watches = g_list_prepend(iface_group_watches, w); g_static_mutex_unlock(&iface_group_watches_lock); /* add all already registered interfaces */ z_ifmon_iterate_ifaces(group, w, Z_IFC_ADD); return w; } void z_ifmon_unregister_group_watch(ZIfmonGroupWatch *watch) { g_static_mutex_lock(&iface_group_watches_lock); iface_watches = g_list_remove(iface_watches, watch); g_static_mutex_unlock(&iface_group_watches_lock); z_ifmon_group_watch_free(watch); } #ifndef IFLA_IFGROUP #define IFLA_IFGROUP 20 #endif /** * Parse ifinfomsg netlink message. * * @param[in] msg netlink message * @param[in] msg_len length of message * @param[out] if_index interface index * @param[out] if_name interface name, only set if the message contains it, otherwise NULL * @param[out] if_flags interface flags * @param[out] if_group interface group, only set if the message contains it, otherwise 0 * * @note We assume no interface can have a group of 0 so we use that to signal that it has no group. The reason for this assumption is: *
 * # ip link set ipsec3 group 0
 * Error: argument "0" is wrong: "group" value is invalid
 * 
* * @returns TRUE on success **/ static inline gboolean z_ifmon_parse_ifinfo(const gchar *msg, gsize msg_len, guint *if_index, const gchar **if_name, guint16 *if_flags, guint32 *if_group) { struct nlmsghdr *h = (struct nlmsghdr *) msg; struct ifinfomsg *ifi; struct rtattr *rta; gint len; /* netlink header */ if (!NLMSG_OK(h, msg_len)) return FALSE; /* ifinfo header */ ifi = NLMSG_DATA(h); *if_index = ifi->ifi_index; *if_flags = ifi->ifi_flags; /* attribute list */ rta = IFLA_RTA(ifi); len = IFLA_PAYLOAD(h); /* make sure we don't return garbage */ *if_name = NULL; *if_group = 0; while (RTA_OK(rta, len)) { switch (rta->rta_type) { case IFLA_IFNAME: *if_name = (gchar *) RTA_DATA(rta); break; case IFLA_IFGROUP: *if_group = *(guint32 *) RTA_DATA(rta); break; } rta = RTA_NEXT(rta, len); } if (len != 0) { z_log(NULL, CORE_ERROR, 1, "Error parsing ifinfomsg netlink message;"); return FALSE; } return TRUE; } static inline gboolean z_ifmon_parse_ifaddr(const gchar *msg, gsize msg_len, guint *ifa_index, guint *ifa_family, const gchar **ifa_addr) { struct nlmsghdr *h = (struct nlmsghdr *) msg; struct ifaddrmsg *ifa; struct rtattr *rta; gint len; /* netlink header */ if (!NLMSG_OK(h, msg_len)) return FALSE; /* ifaddr header */ ifa = NLMSG_DATA(h); *ifa_index = ifa->ifa_index; *ifa_family = ifa->ifa_family; /* attribute list */ rta = IFA_RTA(ifa); len = IFA_PAYLOAD(h); while (RTA_OK(rta, len)) { switch (rta->rta_type) { case IFA_LOCAL: *ifa_addr = (gchar *) RTA_DATA(rta); break; } rta = RTA_NEXT(rta, len); } if (len != 0) { z_log(NULL, CORE_ERROR, 1, "Error parsing ifaddrmsg netlink message;"); return FALSE; } return TRUE; } static void z_ifmon_add_iface(const gchar *msg, gsize msg_len) { const gchar *if_name = NULL; gchar old_ifname[Z_IFNAME_MAX]; guint32 if_index; guint16 if_flags; guint32 if_group; gboolean new = FALSE; ZIfaceInfo *info; gboolean old_iface_changed, new_iface_changed; if (!z_ifmon_parse_ifinfo(msg, msg_len, &if_index, &if_name, &if_flags, &if_group)) return; /** * @note If no interface group is given in the message, if_group is set to 0 by z_ifmon_parse_ifinfo. * Since we create the new ZIfaceInfo instance with g_new0, it is zeroed and so its group is zeroed. * This means the group check below won't call group watchers if there's no group. **/ info = g_hash_table_lookup(iface_hash, &if_index); if (!info) { info = g_new0(ZIfaceInfo, 1); info->index = if_index; g_hash_table_insert(iface_hash, &info->index, info); new = TRUE; } /* interface is changed if: * - it is new * - has its name changed (then both old and new iface is changed) * - has its UP flags changed */ /* interface was renamed, old_iface refers to the old name */ old_iface_changed = !new && if_name && strcmp(info->name, if_name) != 0; /* interface with the new/current name was changed */ new_iface_changed = new || ((info->flags & IFF_UP) != (guint32) (if_flags & IFF_UP)) || old_iface_changed; /* send notification */ if (old_iface_changed) { /* interface was renamed, remove all IPs associated with old * name, provided it was in UP state */ if (info->flags & IFF_UP) z_ifmon_iterate_addrs(info, Z_IFC_REMOVE); } /* update information */ g_strlcpy(old_ifname, info->name, sizeof(old_ifname)); if (if_name) g_strlcpy(info->name, if_name, sizeof(info->name)); info->flags = if_flags; if (new_iface_changed) { if (new) { /* new interface, immediately in UP state, is it possible at all? */ if (info->flags & IFF_UP) z_ifmon_iterate_addrs(info, Z_IFC_ADD); } else if (old_iface_changed) { /* interface was renamed, all add IPs associated with new name, if the interface is up */ if (info->flags & IFF_UP) z_ifmon_iterate_addrs(info, Z_IFC_ADD); } else if ((info->flags & IFF_UP) == 0) { /* interface was downed */ z_ifmon_iterate_addrs(info, Z_IFC_REMOVE); } else if ((info->flags & IFF_UP) != 0) { /* interface was upped */ z_ifmon_iterate_addrs(info, Z_IFC_ADD); } } /* see note below z_ifmon_parse_ifinfo call on why this works if there's no group */ if (info->group != if_group) { /* interface group change, remove the old assignment, add the new */ if (info->group) z_ifmon_call_group_watchers(info->group, Z_IFC_REMOVE, info->name); info->group = if_group; z_ifmon_call_group_watchers(info->group, Z_IFC_ADD, info->name); } if (new) { z_rtnetlink_request_dump(RTM_GETADDR, PF_PACKET); z_log(NULL, CORE_INFO, 4, "Interface added; if_index='%d', if_name='%s', if_flags='%d'", if_index, if_name ? if_name : "unknown", if_flags); } else { z_log(NULL, CORE_INFO, 4, "Interface info updated; if_index='%d', if_name='%s', if_flags='0x%x', if_group='0x%x'", if_index, if_name ? if_name : info->name, if_flags, info->group); } } static void z_ifmon_del_iface(const gchar *msg, gsize msg_len) { ZIfaceInfo *info; guint if_index; const gchar *if_name; guint32 if_group; guint16 if_flags; if (!z_ifmon_parse_ifinfo(msg, msg_len, &if_index, &if_name, &if_flags, &if_group)) return; info = g_hash_table_lookup(iface_hash, &if_index); if (!info) { z_log(NULL, CORE_ERROR, 1, "Interface removal message received, but no such interface known; if_index='%d', if_name='%s'", if_index, if_name ? if_name : "unknown"); return; } z_log(NULL, CORE_INFO, 4, "Interface removed; if_index='%d', if_name='%s', if_group='0x%x'", info->index, info->name, info->group); if ((info->flags & IFF_UP) != 0) z_ifmon_iterate_addrs(info, Z_IFC_REMOVE); if (info->group) z_ifmon_call_group_watchers(info->group, Z_IFC_REMOVE, info->name); g_hash_table_remove(iface_hash, &if_index); } static void z_ifmon_change_iface_addr(const gchar *msg, gsize msg_len) { gint nl_type = ((struct nlmsghdr *) msg)->nlmsg_type; guint ifa_index; guint ifa_family; const gchar *ifa_addr = NULL; struct in_addr *ina; ZIfaceInfo *info; gchar buf[32]; gint i; if (!z_ifmon_parse_ifaddr(msg, msg_len, &ifa_index, &ifa_family, &ifa_addr)) return; if (ifa_family != AF_INET) return; info = g_hash_table_lookup(iface_hash, &ifa_index); if (!info) { z_log(NULL, CORE_INFO, 4, "Interface address message received, but no such interface known; if_index='%d'", ifa_index); return; } ina = (struct in_addr *) ifa_addr; for (i = 0; i < info->in4_address_count; i++) { if (info->in4_addresses[i].s_addr == ina->s_addr) break; } if (i == info->in4_address_count) { /* not found */ if (nl_type == RTM_NEWADDR) { if (i >= Z_IFADDR_MAX) { z_log(NULL, CORE_ERROR, 1, "Maximum number of addresses assigned to single interface is reached; ifaddr_max='%d'", Z_IFADDR_MAX); return; } info->in4_addresses[i] = *ina; info->in4_address_count++; z_log(NULL, CORE_INFO, 4, "Address added to interface; if_name='%s', if_addr='%s'", info->name, z_inet_ntoa(buf, sizeof(buf), *ina)); if (info->flags & IFF_UP) z_ifmon_call_watchers(info->name, Z_IFC_ADD, AF_INET, (gchar *) ina); } else if (nl_type == RTM_DELADDR) { z_log(NULL, CORE_ERROR, 1, "Address removal message referred to a non-existent address;"); } } else { /* found */ if (nl_type == RTM_DELADDR) { z_log(NULL, CORE_INFO, 4, "Address removed from interface; if_name='%s', if_addr='%s'", info->name, z_inet_ntoa(buf, sizeof(buf), *ina)); memmove(info->in4_addresses + i, info->in4_addresses + i + 1, (info->in4_address_count - i) * sizeof(info->in4_addresses[0])); info->in4_address_count--; if (info->flags & IFF_UP) z_ifmon_call_watchers(info->name, Z_IFC_REMOVE, AF_INET, (gchar *) ina); } } } static const void* z_ifmon_get_primary_address_impl(const ZIfaceInfo *info, gint family) { if (info == NULL || family != AF_INET || info->in4_address_count == 0) { return NULL; } return info->in4_addresses; } const void * z_ifmon_get_primary_address_by_name(const gchar *iface, gint family) { return z_ifmon_get_primary_address_impl(z_ifmon_find_by_name(iface), family); } const void * z_ifmon_get_primary_address(guint ifindex, gint family) { return z_ifmon_get_primary_address_impl(g_hash_table_lookup(iface_hash, &ifindex), family); } gboolean z_ifmon_get_ifindex(const gchar *iface, guint *if_index) { ZIfaceInfo *info = z_ifmon_find_by_name(iface); if (info) { *if_index = info->index; return TRUE; } return FALSE; } guint z_ifmon_get_iface_flags(guint ifindex) { ZIfaceInfo *info = g_hash_table_lookup(iface_hash, &ifindex); return info ? info->flags : 0; } void z_ifmon_init(void) { z_netlink_init(); iface_hash = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, g_free); z_netlink_register(RTM_NEWLINK, z_ifmon_add_iface); z_netlink_register(RTM_DELLINK, z_ifmon_del_iface); z_netlink_register(RTM_NEWADDR, z_ifmon_change_iface_addr); z_netlink_register(RTM_DELADDR, z_ifmon_change_iface_addr); z_rtnetlink_request_dump(RTM_GETLINK, PF_INET); } void z_ifmon_destroy(void) { z_netlink_destroy(); } zorp-3.9.5/lib/kzorp.c000066400000000000000000000035141172670260400146110ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #include #include #include #include gboolean z_kzorp_get_lookup_result(guint8 family, gint fd, struct z_kzorp_lookup_result *result) { socklen_t size = sizeof(*result); int level; z_enter(); switch (family) { case PF_INET: level = SOL_IP; break; case PF_INET6: level = SOL_IPV6; break; default: g_assert_not_reached(); break; } if (getsockopt(fd, level, SO_KZORP_RESULT, result, &size) < 0) { z_log(NULL, CORE_ERROR, 3, "Error querying KZorp lookup result; fd='%d', error='%s'", fd, g_strerror(errno)); z_return(FALSE); } z_return(TRUE); } zorp-3.9.5/lib/modules.c000066400000000000000000000047221172670260400151160ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #define G_MODULE_ERROR_SAFE() (g_module_error() ? g_module_error() : "(null)") /** * z_load_module: * @modname: name of the module to load * * This function opens the module specified by @modname as a shared object * and initializes it by calling its zorp_module_init function. * * Returns TRUE on success **/ gint z_load_module(gchar *modname) { GModule *m; gchar *buf; gint (*modinit)(void) __attribute__((may_alias)); z_enter(); buf = g_module_build_path(ZORP_LIBDIR, modname); m = g_module_open(buf, 0); if (m && g_module_symbol(m, "zorp_module_init", (gpointer *) &modinit) && modinit()) { /*LOG This message serves informational purposes, and indicates that the given module was successfully loaded from the given shared object. */ z_log(NULL, CORE_DEBUG, 8, "Module successfully loaded; module='%s', file='%s'", modname, buf); g_free(buf); z_return(TRUE); } /*LOG This message indicates that loading a proxy module failed. */ z_log(NULL, CORE_ERROR, 1, "Module loading failed; module='%s', file='%s', error='%s'", modname, buf, G_MODULE_ERROR_SAFE()); g_free(buf); z_return(FALSE); } zorp-3.9.5/lib/notification.c000066400000000000000000000000001172670260400161150ustar00rootroot00000000000000zorp-3.9.5/lib/plugsession.c000066400000000000000000000555671172670260400160360ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : bazsi * Auditor : * Last audited version: 1.1 * Notes: * ***************************************************************************/ #include #include #include #include /* FIXME: should be run-time configurable */ #define MAX_READ_AT_A_TIME 30 typedef struct _ZPlugIOBuffer { gchar *buf; gsize ofs, end; gsize packet_count, packet_bytes; } ZPlugIOBuffer; struct _ZPlugSession { ZRefCount ref_count; ZPlugSessionData *session_data; ZPoll *poll; ZStream *endpoints[EP_MAX]; ZStackedProxy *stacked; ZPlugIOBuffer buffers[EP_MAX]; ZPlugIOBuffer downbufs[EP_MAX]; gint eofmask; GSource *timeout; GSource *stats_timeout; GTimeVal started_time; guint global_packet_count; gpointer *user_data; gboolean started; }; /* possible eofmask values */ #define EOF_CLIENT_R 0x0001 #define EOF_SERVER_R 0x0002 #define EOF_CLIENT_W 0x0004 #define EOF_SERVER_W 0x0008 #define EOF_CLIENT_REMOVED 0x0010 #define EOF_SERVER_REMOVED 0x0020 #define EOF_DESTROYED 0x0040 #define EOF_ALL 0x000f static void z_plug_update_eof_mask(ZPlugSession *self, guint add_mask) { guint old_mask = self->eofmask; self->eofmask |= add_mask; if ((self->eofmask & (EOF_CLIENT_R | EOF_CLIENT_W | EOF_CLIENT_REMOVED)) == (EOF_CLIENT_R | EOF_CLIENT_W)) { z_poll_remove_stream(self->poll, self->endpoints[EP_CLIENT]); self->eofmask |= EOF_CLIENT_REMOVED; } if ((self->eofmask & (EOF_SERVER_R | EOF_SERVER_W | EOF_SERVER_REMOVED)) == (EOF_SERVER_R | EOF_SERVER_W)) { z_poll_remove_stream(self->poll, self->endpoints[EP_SERVER]); self->eofmask |= EOF_SERVER_REMOVED; } if ((self->eofmask & (EOF_DESTROYED | EOF_CLIENT_REMOVED | EOF_SERVER_REMOVED)) == (EOF_CLIENT_REMOVED | EOF_SERVER_REMOVED)) { z_plug_session_cancel(self); self->eofmask |= EOF_DESTROYED; } /*LOG This message reports that the end-of-file status has been updated. */ z_log(NULL, CORE_DEBUG, 7, "eofmask updated; old_mask='%04x', eof_mask='%04x'", old_mask, self->eofmask); if (!(old_mask & EOF_DESTROYED) && (self->eofmask & EOF_DESTROYED)) { /* WARNING: calling ->finish() may have freed _this_ PlugSession instance! It's forbidden to touch self after this call! */ if (self->session_data->finish) self->session_data->finish(self, self->user_data); } } static guint z_plug_read_input(ZPlugSession *self, ZStream *input, ZPlugIOBuffer *buf) { GIOStatus rc; z_enter(); rc = z_stream_read(input, buf->buf, self->session_data->buffer_size, &buf->end, NULL); if (rc == G_IO_STATUS_NORMAL) { buf->packet_bytes += buf->end; buf->packet_count++; self->global_packet_count++; if (self->session_data->packet_stats_interval_packet && (self->global_packet_count % self->session_data->packet_stats_interval_packet) == 0) { if (!self->session_data->packet_stats(self, self->buffers[EP_CLIENT].packet_bytes, self->buffers[EP_CLIENT].packet_count, self->buffers[EP_SERVER].packet_bytes, self->buffers[EP_SERVER].packet_count, self->user_data)) { z_plug_update_eof_mask(self, EOF_ALL); rc = G_IO_STATUS_EOF; } } } z_return(rc); } static GIOStatus z_plug_write_output(ZPlugSession *self G_GNUC_UNUSED, ZPlugIOBuffer *buf, ZStream *output) { GIOStatus rc; gsize bytes_written; z_enter(); if (buf->ofs != buf->end) { /* buffer not empty */ rc = z_stream_write(output, &buf->buf[buf->ofs], buf->end - buf->ofs, &bytes_written, NULL); switch (rc) { case G_IO_STATUS_NORMAL: buf->ofs += bytes_written; break; case G_IO_STATUS_AGAIN: break; default: z_return(rc); } if (buf->ofs != buf->end) { z_stream_set_cond(output, G_IO_OUT, TRUE); z_leave(); z_return(G_IO_STATUS_AGAIN); } } z_return(G_IO_STATUS_NORMAL); } static GIOStatus z_plug_copy_data(ZPlugSession *self, ZStream *from, ZStream *to, ZPlugIOBuffer *buf) { GIOStatus rc = G_IO_STATUS_ERROR; int pkt_count = 0; z_enter(); if (self->timeout) z_timeout_source_set_timeout(self->timeout, self->session_data->timeout); if (!from || !buf) z_return(G_IO_STATUS_ERROR); z_stream_set_cond(from, G_IO_IN, FALSE); if (to) { z_stream_set_cond(to, G_IO_OUT, FALSE); rc = z_plug_write_output(self, buf, to); if (rc != G_IO_STATUS_NORMAL) z_return(rc); } while (pkt_count < MAX_READ_AT_A_TIME) { buf->ofs = buf->end = 0; rc = z_plug_read_input(self, from, buf); if (rc == G_IO_STATUS_NORMAL) { if (to) { rc = z_plug_write_output(self, buf, to); if (rc == G_IO_STATUS_AGAIN) break; else if (rc != G_IO_STATUS_NORMAL) z_return(rc); } } else if (rc == G_IO_STATUS_AGAIN) break; else if (rc == G_IO_STATUS_EOF) z_return(rc); else z_return(G_IO_STATUS_ERROR); pkt_count++; } if (buf->ofs == buf->end) z_stream_set_cond(from, G_IO_IN, TRUE); z_return(rc); } /* callbacks when no stacking is made */ static gboolean z_plug_copy_client_to_server(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; gboolean ret; z_enter(); if (self->session_data->copy_to_server) ret = z_plug_copy_data(self, self->endpoints[EP_CLIENT], self->endpoints[EP_SERVER], &self->buffers[EP_SERVER]); else ret = z_plug_copy_data(self, self->endpoints[EP_CLIENT], NULL, &self->buffers[EP_SERVER]); switch (ret) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: break; case G_IO_STATUS_EOF: if (self->session_data->shutdown_soft) { z_stream_shutdown(self->endpoints[EP_CLIENT], SHUT_RD, NULL); z_stream_shutdown(self->endpoints[EP_SERVER], SHUT_WR, NULL); z_plug_update_eof_mask(self, EOF_CLIENT_R | EOF_SERVER_W); } else { z_plug_update_eof_mask(self, EOF_ALL); } break; default: z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } z_return(TRUE); } static gboolean z_plug_copy_server_to_client(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; GIOStatus ret; z_enter(); if (self->session_data->copy_to_client) ret = z_plug_copy_data(self, self->endpoints[EP_SERVER], self->endpoints[EP_CLIENT], &self->buffers[EP_CLIENT]); else ret = z_plug_copy_data(self, self->endpoints[EP_SERVER], NULL, &self->buffers[EP_CLIENT]); switch (ret) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: break; case G_IO_STATUS_EOF: if (self->session_data->shutdown_soft) { z_stream_shutdown(self->endpoints[EP_SERVER], SHUT_RD, NULL); z_stream_shutdown(self->endpoints[EP_CLIENT], SHUT_WR, NULL); z_plug_update_eof_mask(self, EOF_SERVER_R | EOF_CLIENT_W); } else { z_plug_update_eof_mask(self, EOF_ALL); } break; default: z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } z_return(TRUE); } /* callbacks when a stacked module exists */ static gboolean z_plug_copy_client_to_down(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; GIOStatus ret; z_enter(); if (self->session_data->copy_to_server) ret = z_plug_copy_data(self, self->endpoints[EP_CLIENT], self->stacked->downstreams[EP_CLIENT], &self->downbufs[EP_CLIENT]); else ret = z_plug_copy_data(self, self->endpoints[EP_CLIENT], NULL, &self->downbufs[EP_CLIENT]); switch (ret) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: break; case G_IO_STATUS_EOF: if (self->session_data->shutdown_soft) { z_stream_shutdown(self->endpoints[EP_CLIENT], SHUT_RD, NULL); z_stream_shutdown(self->stacked->downstreams[EP_CLIENT], SHUT_WR, NULL); z_plug_update_eof_mask(self, EOF_CLIENT_R); } else { z_plug_update_eof_mask(self, EOF_ALL); } break; default: z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } z_return(TRUE); } static gboolean z_plug_copy_down_to_client(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; GIOStatus ret; z_enter(); ret = z_plug_copy_data(self, self->stacked->downstreams[EP_CLIENT], self->endpoints[EP_CLIENT], &self->buffers[EP_CLIENT]); switch (ret) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: break; case G_IO_STATUS_EOF: if (self->session_data->shutdown_soft) { z_stream_shutdown(self->stacked->downstreams[EP_CLIENT], SHUT_RD, NULL); z_stream_shutdown(self->endpoints[EP_CLIENT], SHUT_WR, NULL); z_plug_update_eof_mask(self, EOF_CLIENT_W); } else { z_plug_update_eof_mask(self, EOF_ALL); } break; default: z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } z_return(TRUE); } static gboolean z_plug_copy_server_to_down(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; GIOStatus ret; z_enter(); if (self->session_data->copy_to_client) ret = z_plug_copy_data(self, self->endpoints[EP_SERVER], self->stacked->downstreams[EP_SERVER], &self->downbufs[EP_SERVER]); else ret = z_plug_copy_data(self, self->endpoints[EP_SERVER], NULL, &self->downbufs[EP_SERVER]); switch (ret) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: break; case G_IO_STATUS_EOF: if (self->session_data->shutdown_soft) { z_stream_shutdown(self->endpoints[EP_SERVER], SHUT_RD, NULL); z_stream_shutdown(self->stacked->downstreams[EP_SERVER], SHUT_WR, NULL); z_plug_update_eof_mask(self, EOF_SERVER_R); } else { z_plug_update_eof_mask(self, EOF_ALL); } break; default: z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } z_return(TRUE); } static gboolean z_plug_copy_down_to_server(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; GIOStatus ret; z_enter(); ret = z_plug_copy_data(self, self->stacked->downstreams[EP_SERVER], self->endpoints[EP_SERVER], &self->buffers[EP_SERVER]); switch (ret) { case G_IO_STATUS_NORMAL: case G_IO_STATUS_AGAIN: z_return(TRUE); case G_IO_STATUS_EOF: if (self->session_data->shutdown_soft) { z_stream_shutdown(self->stacked->downstreams[EP_SERVER], SHUT_RD, NULL); z_stream_shutdown(self->endpoints[EP_SERVER], SHUT_WR, NULL); z_plug_update_eof_mask(self, EOF_SERVER_W); } else { z_plug_update_eof_mask(self, EOF_ALL); } break; default: z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } z_return(TRUE); } gboolean z_plug_timeout(gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; z_enter(); if (self->session_data->timeout_cb) self->session_data->timeout_cb (self, self->user_data); z_plug_update_eof_mask(self, EOF_ALL); z_return(FALSE); } /* FIXME: merge these two functions */ gboolean z_plug_session_init_streams(ZPlugSession *self) { z_enter(); self->buffers[EP_CLIENT].buf = g_new0(char, self->session_data->buffer_size); self->buffers[EP_SERVER].buf = g_new0(char, self->session_data->buffer_size); z_stream_set_nonblock(self->endpoints[EP_CLIENT], TRUE); z_stream_set_callback(self->endpoints[EP_CLIENT], G_IO_IN, z_plug_copy_client_to_server, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->endpoints[EP_CLIENT], G_IO_OUT, z_plug_copy_server_to_client, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_cond(self->endpoints[EP_CLIENT], G_IO_IN, TRUE); z_stream_set_timeout(self->endpoints[EP_CLIENT], -2); z_stream_set_nonblock(self->endpoints[EP_SERVER], TRUE); z_stream_set_callback(self->endpoints[EP_SERVER], G_IO_IN, z_plug_copy_server_to_client, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->endpoints[EP_SERVER], G_IO_OUT, z_plug_copy_client_to_server, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_cond(self->endpoints[EP_SERVER], G_IO_IN, TRUE); z_stream_set_timeout(self->endpoints[EP_SERVER], -2); z_poll_add_stream(self->poll, self->endpoints[EP_CLIENT]); z_poll_add_stream(self->poll, self->endpoints[EP_SERVER]); z_return(TRUE); } static gboolean z_plug_session_init_stacked_streams(ZPlugSession *self) { z_enter(); if (self->stacked) { self->downbufs[EP_CLIENT].buf = g_new0(char, self->session_data->buffer_size); self->downbufs[EP_SERVER].buf = g_new0(char, self->session_data->buffer_size); z_stream_set_callback(self->endpoints[EP_CLIENT], G_IO_IN, z_plug_copy_client_to_down, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->endpoints[EP_CLIENT], G_IO_OUT, z_plug_copy_down_to_client, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->endpoints[EP_SERVER], G_IO_IN, z_plug_copy_server_to_down, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->endpoints[EP_SERVER], G_IO_OUT, z_plug_copy_down_to_server, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->stacked->downstreams[EP_CLIENT], G_IO_IN, z_plug_copy_down_to_client, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->stacked->downstreams[EP_CLIENT], G_IO_OUT, z_plug_copy_client_to_down, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_cond(self->stacked->downstreams[EP_CLIENT], G_IO_IN, TRUE); z_stream_set_callback(self->stacked->downstreams[EP_SERVER], G_IO_IN, z_plug_copy_down_to_server, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_callback(self->stacked->downstreams[EP_SERVER], G_IO_OUT, z_plug_copy_server_to_down, z_plug_session_ref(self), (GDestroyNotify) z_plug_session_unref); z_stream_set_cond(self->stacked->downstreams[EP_SERVER], G_IO_IN, TRUE); z_poll_add_stream(self->poll, self->stacked->downstreams[EP_CLIENT]); z_poll_add_stream(self->poll, self->stacked->downstreams[EP_SERVER]); } z_return(TRUE); } static gboolean z_plug_session_stats_timeout(gpointer user_data) { ZPlugSession *self = (ZPlugSession *) user_data; if (self->session_data->packet_stats) { if (!self->session_data->packet_stats(self, self->buffers[EP_CLIENT].packet_bytes, self->buffers[EP_CLIENT].packet_count, self->buffers[EP_SERVER].packet_bytes, self->buffers[EP_SERVER].packet_count, self->user_data)) { z_plug_update_eof_mask(self, EOF_ALL); } z_return(TRUE); } /*LOG This message indicates that packet stats interval was specified, but no action was configured to handle the event. Check your policy for packetStats event. */ z_log(NULL, CORE_ERROR, 3, "Packet stats timeout elapsed, and no timeout callback specified;"); z_return(FALSE); } static ZPolicyObj * z_plug_session_query_bandwidth(ZPlugSession *self, gchar *name, gpointer value G_GNUC_UNUSED) { GTimeVal now, spent; double bandwidth = 0.0; g_get_current_time(&now); spent.tv_sec = now.tv_sec - self->started_time.tv_sec; spent.tv_usec = now.tv_usec - self->started_time.tv_usec; if (spent.tv_usec < -500000) spent.tv_sec++; if (strcmp(name, "bandwidth_to_client") == 0) { bandwidth = (double) self->buffers[EP_CLIENT].packet_bytes / spent.tv_sec; } else if (strcmp(name, "bandwidth_to_server") == 0) { bandwidth = (double) self->buffers[EP_SERVER].packet_bytes / spent.tv_sec; } return z_policy_var_build("d", bandwidth); } void z_plug_session_register_vars(ZPlugSession *self, ZPolicyDict *dict) { z_policy_dict_register(dict, Z_VT_CUSTOM, "bandwidth_to_client", Z_VF_READ, /* value, get, set, free, userdata, destroy-notify */ NULL, z_plug_session_query_bandwidth, NULL, NULL, self, NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "bandwidth_to_server", Z_VF_READ, NULL, z_plug_session_query_bandwidth, NULL, NULL, self, NULL); } gboolean z_plug_session_start(ZPlugSession *self, ZPoll *poll) { if (self->started) g_assert_not_reached(); z_poll_ref(poll); self->poll = poll; if (z_plug_session_init_streams(self) && z_plug_session_init_stacked_streams(self)) { g_get_current_time(&self->started_time); if (self->session_data->packet_stats_interval_time > 0) { GMainContext *context; self->stats_timeout = g_timeout_source_new(self->session_data->packet_stats_interval_time); g_source_set_callback(self->stats_timeout, z_plug_session_stats_timeout, self, NULL); context = z_poll_get_context(self->poll); g_source_attach(self->stats_timeout, context); } if (self->session_data->timeout > 0) { GMainContext *context; self->timeout = z_timeout_source_new(self->session_data->timeout); g_source_set_callback(self->timeout, z_plug_timeout, self, NULL); context = z_poll_get_context(self->poll); g_source_attach(self->timeout, context); } self->started = TRUE; return TRUE; } return FALSE; } void z_plug_session_cancel(ZPlugSession *self) { gint i; if (!self->started) return; for (i = EP_CLIENT; i < EP_MAX; i++) { if (self->stacked) { z_poll_remove_stream(self->poll, self->stacked->downstreams[i]); } z_poll_remove_stream(self->poll, self->endpoints[i]); } if (self->stacked) { z_stacked_proxy_destroy(self->stacked); self->stacked = NULL; } if (self->stats_timeout) { g_source_destroy(self->stats_timeout); g_source_unref(self->stats_timeout); self->stats_timeout = NULL; } if (self->timeout) { g_source_destroy(self->timeout); g_source_unref(self->timeout); self->timeout = NULL; } self->started = FALSE; } static void z_plug_session_free(ZPlugSession *self) { g_free(self); } ZPlugSession * z_plug_session_ref(ZPlugSession *self) { z_refcount_inc(&self->ref_count); return self; } void z_plug_session_unref(ZPlugSession *self) { if (z_refcount_dec(&self->ref_count)) z_plug_session_free(self); } ZPlugSession * z_plug_session_new(ZPlugSessionData *session_data, ZStream *client_stream, ZStream *server_stream, ZStackedProxy *stacked, gpointer user_data) { ZPlugSession *self = g_new0(ZPlugSession, 1); gchar buf[Z_STREAM_MAX_NAME]; self->user_data = user_data; z_stream_ref(client_stream); z_stream_ref(server_stream); if (!client_stream->name[0]) { g_snprintf(buf, sizeof(buf), "%s/%s", fake_session_id, "client"); z_stream_set_name(client_stream, buf); } if (!server_stream->name[0]) { g_snprintf(buf, sizeof(buf), "%s/%s", fake_session_id, "server"); z_stream_set_name(server_stream, buf); } self->endpoints[EP_CLIENT] = client_stream; self->endpoints[EP_SERVER] = server_stream; self->stacked = stacked; self->session_data = session_data; z_refcount_set(&self->ref_count, 1); return self; } void z_plug_session_destroy(ZPlugSession *self) { gint i; if (self) { g_assert(!self->started); for (i = EP_CLIENT; i < EP_MAX; i++) { if (self->downbufs[i].buf) { g_free(self->downbufs[i].buf); self->downbufs[i].buf = NULL; } g_free(self->buffers[i].buf); self->buffers[i].buf = NULL; z_stream_unref(self->endpoints[i]); self->endpoints[i] = NULL; } z_poll_unref(self->poll); self->poll = NULL; z_plug_session_unref(self); } } zorp-3.9.5/lib/proxy.c000066400000000000000000001335641172670260400146360ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : kisza * Last audited version: 1.23 * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * References between child and parent proxies work as follows * * When a proxy wants to stack a child proxy a ZStackedProxy structure is * created which contains the stream objects towards the child, and also * contains references to both the proxy and its child. These references are * removed when the ZStackedProxy structure is freed using * z_stacked_proxy_destroy. * * When the child proxy starts up it adds a reference to its parent still in * the parent proxy's thread using z_proxy_add_child() in z_proxy_new(). * This adds a reference from child to parent through its parent_proxy * field, and from parent to child through its child_proxies list. This is a * circular reference. * * 1) The child proxy exits first * * When the child proxy exits it calls z_proxy_destroy() which in turn * calls z_proxy_set_parent(NULL) which drops the reference to its * parent. The circular reference is now resolved. The parent will detect * that its child exited (probably one of the streams indicates EOF) and * calls z_stacked_proxy_destroy() for the ZStackedProxy structure * associated with this child. It closes and frees streams, and removes * the child proxy from the parent->child_proxies list. * * 2) The parent proxy exits first * * It is assumed that ZStackedProxy structures associated with any child * proxy is freed prior to calling z_proxy_destroy(). This assumption is * valid as ZTransfer or proxy specific transfer code calls * z_stacked_proxy_destroy(), thus the only remaining reference to the * child proxy instance is through child_proxies. * * When the parent proxy exits, it calls z_proxy_destroy() which in turn * frees every item on its child_proxies list. * * 3) The parent and child proxies exit at the same time * * In this case the exit is not synchronized by the EOF the parent reads * from the child. Thus z_stacked_proxy_destroy() and z_proxy_destroy() * might race on the following (possibly shared) data accesses: * z_stacked_proxy_destroy: * child is removed from parent->child_list * z_proxy_destroy (parent): * child is removed from parent->child_list (if present) * z_proxy_destroy (child): * child->parent is set to NULL * * Synchronization during reference counting * * The general rule is that every proxy instance modifies its own data * fields only in order to avoid locking. The only locks used are the * recursive mutexes protecting the reference counts. The only exception to * this rule happens when the child proxy starts up and adds itself to its * parent's child_proxies list. The synchronization here is also simple as * this happens in the parent proxy's thread, thus no locks are necessary. * * Interface list locking * * The interface list is manipulated from two threads simoultaneously: * 1) the parent might add/remove interface to the list anytime * 2) the child queries the list when it wants to communicate with the parent * * A race might occur when the list is being deleted and the child wants to * communicate with the parent (for example: parent exits at the same time * the child wants to call set_verdict). This is resolved by using a * GStaticMutex in ZProxy called interfaces_lock. It is assumed that the * ZProxyIface class does not touch the interfaces_lock in its destructor as * in that case a deadlock might occur. */ /* * This hashtable contains the ZProxy instances indexed by their session_id. * It is used by the SZIG code to look be able to communicate with actual * proxies. */ static GStaticMutex proxy_hash_mutex = G_STATIC_MUTEX_INIT; static GHashTable *proxy_hash = NULL; /** * z_proxy_get_service_session_id: * @self: the proxy * * Get the session_id of the service instance where the specified proxy * instance belongs to. Each session may contain a stack of proxies, each * with a different session id. The first parts of these proxy specific * session IDs are the same. This function returns that. * * For example, the proxy might have the session_id of svc/ssh:0/ssh, the * service specific session id is the first two parts, e.g. svc/ssh:0. * * NOTE: The returned string is duplicated. * * Returns: th service session id * */ static gchar * z_proxy_get_service_session_id(ZProxy *self) { gchar *proxy_session; gint len; g_assert(self->session_id != NULL); proxy_session = strrchr(self->session_id, '/'); g_assert(proxy_session != NULL); len = proxy_session - self->session_id; return g_strndup(self->session_id, len); } /** * z_proxy_register: * @self: The proxy to be registered * * It registers the proxy instance in the proxy_hash, based on its session name. */ static void z_proxy_register(ZProxy *self) { gchar *session_id; GList *list = NULL; GList *list_new = NULL; session_id = z_proxy_get_service_session_id(self); g_static_mutex_lock(&proxy_hash_mutex); list = g_hash_table_lookup(proxy_hash, session_id); z_proxy_ref(self); list_new = g_list_prepend(list, self); if (list_new != list) { /* NOTE: frees the old session_id in the hash, but leaves the list intact */ g_hash_table_remove(proxy_hash, session_id); g_hash_table_insert(proxy_hash, session_id, list_new); } else g_free(session_id); g_static_mutex_unlock(&proxy_hash_mutex); } /** * z_proxy_unregister: * @self: the proxy to be unregistered * * Unregisters the proxy. If the proxy list is no longer used, it is destroyed */ static void z_proxy_unregister(ZProxy *self) { gchar *session_id; GList *list, *list_new; session_id = z_proxy_get_service_session_id(self); g_static_mutex_lock(&proxy_hash_mutex); list = g_hash_table_lookup(proxy_hash, session_id); list_new = g_list_remove(list, self); z_proxy_unref(self); if (list != list_new) { g_hash_table_remove(proxy_hash, session_id); if (list_new) g_hash_table_insert(proxy_hash, session_id, list_new); else g_free(session_id); } else { g_free(session_id); } g_static_mutex_unlock(&proxy_hash_mutex); } /** * z_proxy_stop_req_cb: * @self: proxy instance * @user_data: not used * * Sets the stop request flag for the proxy */ static void z_proxy_stop_req_cb(gpointer s, gpointer user_data G_GNUC_UNUSED) { ZProxy *self = (ZProxy *)s; self->flags |= ZPF_STOP_REQUEST; z_proxy_wakeup(self); } /** * z_proxy_stop_request: * @session_id: proxy thread's session_id * * Sets the stop request flag for each proxy * * Returns: TRUE if the proxy list found, FALSE otherwise * */ gboolean z_proxy_stop_request(const gchar *session_id) { GList *list; gboolean verdict = FALSE; g_static_mutex_lock(&proxy_hash_mutex); list = g_hash_table_lookup(proxy_hash, session_id); if (list) { g_list_foreach(list, z_proxy_stop_req_cb, NULL); verdict = TRUE; } g_static_mutex_unlock(&proxy_hash_mutex); return verdict; } /** * z_proxy_hash_unref_proxy: * @key: not used * @value: GList instance * @user_data: not used * * unrefs all proxy in the list */ static void z_proxy_hash_unref_proxy(gpointer key G_GNUC_UNUSED, gpointer value, gpointer user_data G_GNUC_UNUSED) { GList *list = value, *l; for (l = list; l; l = l->next) z_proxy_unref((ZProxy *) l->data); g_list_free(list); } /** * z_proxy_hash_init: * * Initalizes the proxy list */ void z_proxy_hash_init(void) { g_static_mutex_lock(&proxy_hash_mutex); proxy_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); g_static_mutex_unlock(&proxy_hash_mutex); } /** * z_proxy_hash_destroy: * * Deinitializes proxy list */ void z_proxy_hash_destroy(void) { g_static_mutex_lock(&proxy_hash_mutex); if (proxy_hash) { g_hash_table_foreach(proxy_hash, z_proxy_hash_unref_proxy, NULL); g_hash_table_destroy(proxy_hash); proxy_hash = NULL; } g_static_mutex_unlock(&proxy_hash_mutex); } /** * z_proxy_policy_call_event: * @self this #ZProxy instance * @event the called python event * * Thiis function call the @event event from current instance. * * Returns: a boolean value */ static gboolean z_proxy_policy_call_event(ZProxy *self, gchar *event, gchar *old_event_name) { ZPolicyObj *res; gboolean called; /*LOG This message reports that Zorp is about to call the proxy's %event() event. */ z_proxy_log(self, CORE_DEBUG, 7, "calling %s() event;", event); res = z_policy_call(self->handler, event, NULL, &called, self->session_id); if (!called && old_event_name) { static gboolean obsolete_name_logged = FALSE; z_policy_var_unref(res); res = z_policy_call(self->handler, old_event_name, NULL, &called, self->session_id); if (!obsolete_name_logged && called) { obsolete_name_logged = TRUE; z_proxy_log(self, CORE_POLICY, 0, "Obsolete policy handler in Proxy definition; new_name='%s', old_name='%s'", event, old_event_name); } } if (res == NULL && called) { z_proxy_leave(self); return FALSE; } z_policy_var_unref(res); return TRUE; } /** * z_proxy_policy_call: * @self this #ZProxy instance * @event the called python event family * * This function call a python event family. * If event named to "event" it's first call * __pre_event__. If the call was success it's * call event and for last it's call __post_event__. * * Returns: TRUE if all call is success, FALSE otherwise */ static gboolean z_proxy_policy_call(ZProxy *self, gchar *event, gchar *old_event_name) { gchar event_string[512]; z_proxy_enter(self); z_policy_thread_acquire(self->thread); g_snprintf(event_string, sizeof(event_string), "__pre_%s__", event); if (z_proxy_policy_call_event(self, event_string, NULL)) { if (z_proxy_policy_call_event(self, event, old_event_name)) { g_snprintf(event_string, sizeof(event_string), "__post_%s__", event); if (z_proxy_policy_call_event(self, event_string, NULL)) { z_policy_thread_release(self->thread); z_proxy_leave(self); return TRUE; } } } z_policy_thread_release(self->thread); z_proxy_leave(self); return FALSE; } /** * z_proxy_policy_config: * @self: this ZProxy instance * * Acquires the thread associated with this Proxy instance and calls * the __pre_config__, config and __post_config__ events. * **/ gboolean z_proxy_policy_config(ZProxy *self) { z_proxy_enter(self); z_proxy_set_state(self, ZPS_CONFIG); z_policy_struct_set_is_config(self->ssl_opts.ssl_struct, TRUE); if (!z_proxy_policy_call(self, "config", NULL)) { z_proxy_leave(self); return FALSE; } #if 0 // FIXME: readd variable dump z_policy_thread_acquire(self->thread); z_proxy_vars_dump_values(self->vars, self); z_policy_thread_release(self->thread); #endif z_policy_struct_set_is_config(self->ssl_opts.ssl_struct, FALSE); z_proxy_leave(self); return TRUE; } /** * z_proxy_policy_startup: * @self: this ZProxy instance * * Acquires the thread associated with this TProxy instance and calls * the __pre_startup__, startup and __post_startup__ events. **/ gboolean z_proxy_policy_startup(ZProxy *self) { z_proxy_enter(self); z_proxy_set_state(self, ZPS_STARTING_UP); if (!z_proxy_policy_call(self, "startup", "startUp")) { z_proxy_leave(self); return FALSE; } z_proxy_set_state(self, ZPS_WORKING); z_proxy_leave(self); return TRUE; } /** * z_proxy_policy_shutdown: * @self: this ZProxy instance * * Acquires the thread associated with this TProxy instance and calls * the __pre_shutdown__, shutdown and __post_shutdown events. **/ void z_proxy_policy_shutdown(ZProxy *self) { z_proxy_enter(self); z_proxy_set_state(self, ZPS_SHUTTING_DOWN); z_proxy_policy_call(self, "shutdown", "shutDown"); z_proxy_leave(self); } /** * z_proxy_policy_destroy: * @self: this ZProxy instance * * Acquires the thread associated with this TProxy instance and calls * the __destroy__ event. **/ void z_proxy_policy_destroy(ZProxy *self) { ZPolicyObj *res; gboolean called; /* NOTE: this function is also called when thread creation failed, in which case we are unable to call our Python functions */ z_proxy_enter(self); if (z_proxy_get_state(self) > ZPS_THREAD_STARTED) { /*LOG This message reports that Zorp is about to call the proxy's __destroy__() event. This method handles the pre destroy tasks, like the shutdown of the server side connection. */ z_proxy_log(self, CORE_DEBUG, 7, "calling __destroy__() event;"); z_policy_thread_acquire(self->thread); res = z_policy_call(self->handler, "__destroy__", NULL, &called, self->session_id); z_policy_var_unref(res); z_policy_thread_release(self->thread); z_proxy_set_state(self, ZPS_DESTROYING); } z_proxy_leave(self); } /** * z_proxy_set_priority: * @self: ZProxy instance * @pri: new priority * * This function changes the proxy priority in the current process. If the * proxy has its own thread, then its thread priority is changed, if it is a * nonblocking proxy, nothing is changed. * * The current proxy priority is stored in the self->proxy_pri member. **/ void z_proxy_set_priority(ZProxy *self, GThreadPriority pri) { GList *l; if (self->proxy_pri != pri) { if ((self->flags & ZPF_NONBLOCKING) == 0 && self->proxy_thread) { g_thread_set_priority(self->proxy_thread->thread, pri); } for (l = self->child_proxies; l; l = l->next) { if (z_proxy_get_state(l->data) > ZPS_CONFIG && z_proxy_get_state(l->data) < ZPS_SHUTTING_DOWN) z_proxy_set_priority(l->data, pri); } self->proxy_pri = pri; } } static void z_proxy_propagate_channel_props(ZProxy *self G_GNUC_UNUSED) { } static gboolean z_proxy_set_server_address_no_acquire(ZProxy *self, const gchar *host, gint port) { ZPolicyObj *res = NULL; ZPolicyObj *args; gint rc = FALSE; gboolean called; args = z_policy_var_build("(si)", host, port); res = z_policy_call(self->handler, "setServerAddress", args, &called, self->session_id); if (!res) goto out; if (!z_policy_var_parse(res, "i", &rc) || !rc) goto out; rc = TRUE; out: if (res) z_policy_var_unref(res); return rc; } /** * Set server address for using by z_proxy_connect_server * @param self[in,out]: ZProxy instance * @param host[in]: remote server address * @param port: remote server port * @return: TRUE on success, FALSE otherwise */ gboolean z_proxy_set_server_address(ZProxy *self, const gchar *host, gint port) { gint rc = FALSE; z_policy_thread_acquire(self->thread); rc = z_proxy_set_server_address_no_acquire(self, host, port); z_policy_thread_release(self->thread); return rc; } /** * z_proxy_connect_server: * @self: proxy instance * @host: host to connect to, used as a hint by the policy layer but may as well be ignored * @port: port in host to connect to * * Send a connectServer event to the associated policy object. Returns TRUE * if the server-side connection is established, otherwise the * connection to the client should be closed. **/ gboolean z_proxy_connect_server(ZProxy *self, const gchar *host, gint port) { ZPolicyObj *res; gint rc; gboolean called; z_proxy_enter(self); /* It might be possible that we already connected to the server: if the SSL handshake order is server-client, we *do* have to connect to the server before starting the client handshake. If this is the case we simply return the already established connection. */ if (self->endpoints[EP_SERVER] && !z_stream_broken(self->endpoints[EP_SERVER])) { z_proxy_log(self, CORE_INFO, 6, "Using already established server connection;"); z_proxy_return(self, TRUE); } z_proxy_propagate_channel_props(self); if (self->endpoints[EP_SERVER]) { z_stream_shutdown(self->endpoints[EP_SERVER], SHUT_RDWR, NULL); z_stream_close(self->endpoints[EP_SERVER], NULL); z_stream_unref(self->endpoints[EP_SERVER]); self->endpoints[EP_SERVER] = NULL; z_proxy_ssl_clear_session(self, EP_SERVER); } z_policy_thread_acquire(self->thread); if (host && host[0] && !z_proxy_set_server_address_no_acquire(self, host, port)) { z_policy_thread_release(self->thread); z_proxy_return(self, FALSE); } res = z_policy_call(self->handler, "connectServer", NULL, &called, self->session_id); if (res && z_policy_stream_check(res)) { self->endpoints[EP_SERVER] = z_policy_stream_get_stream(res); } else { rc = FALSE; goto error; } z_policy_var_unref(res); z_policy_thread_release(self->thread); z_proxy_propagate_channel_props(self); if (self->endpoints[EP_SERVER]) rc = z_proxy_ssl_init_stream(self, EP_SERVER); else rc = FALSE; z_proxy_return(self, rc); error: z_policy_var_unref(res); z_policy_thread_release(self->thread); z_proxy_propagate_channel_props(self); z_proxy_return(self, FALSE); } /** * z_proxy_user_authenticated: * @self: proxy instance * @entity: the name of the authenticated entity * * This function is called by the proxy when it decides that the user is * authenticated by some inband authentication method. **/ gboolean z_proxy_user_authenticated(ZProxy *self, const gchar *entity, gchar const **groups, ZProxyUserAuthType type) { ZPolicyObj *res = NULL; ZPolicyObj *groups_tuple; gboolean called; gboolean rc = TRUE; z_proxy_enter(self); z_policy_thread_acquire(self->thread); if (groups) { groups_tuple = z_policy_convert_strv_to_list(groups); } else { groups_tuple = z_policy_none_ref(); } gchar *auth_info = ""; switch (type) { case Z_PROXY_USER_AUTHENTICATED_NONE: z_proxy_log(self, CORE_INFO, 6, "Internal problem, NONE authentication should not be used for userAuthenticated function from Zorp; type='%d'", type); auth_info = "none"; break; case Z_PROXY_USER_AUTHENTICATED_INBAND: auth_info = "inband"; break; case Z_PROXY_USER_AUTHENTICATED_GATEWAY: auth_info = "gw-auth"; break; case Z_PROXY_USER_AUTHENTICATED_SERVER: auth_info = "server"; break; } res = z_policy_call(self->handler, "userAuthenticated", z_policy_var_build("(sOs)", entity, groups_tuple, auth_info), &called, self->session_id); z_policy_var_unref(groups_tuple); if (!res) rc = FALSE; z_policy_var_unref(res); z_policy_thread_release(self->thread); z_proxy_leave(self); return rc; } /** * @param self proxy instance * @param protocol the protocol number (ZD_PROTO_*) is returned here * @param client_address the remote address of the client is returned here * @param client_local the local address of the connection to the client is returned here * @param server_address the remote address of the server is returned here * @param server_local the local address of the connection to the server is returned here * @param client_listen the address of the listener which initiated this session is returned here * * This function is used to query the addresses used to connecting the proxy * to the client and server. The utilized application protocol is also * returned and the listener address which accepted the connection. * * NOTE: this function assumes that a Python thread state is acquired. **/ gboolean z_proxy_get_addresses_locked(ZProxy *self, guint *protocol, ZSockAddr **client_address, ZSockAddr **client_local, ZSockAddr **server_address, ZSockAddr **server_local, ZDispatchBind **client_listen) { ZPolicyObj *o; z_proxy_enter(self); if (protocol) { ZPolicyObj *pyproto; pyproto = z_session_getattr(self->handler, "protocol"); if (PyInt_Check(pyproto)) *protocol = PyInt_AsLong(pyproto); else *protocol = ZD_PROTO_TCP; z_policy_var_unref(pyproto); } if (client_address) { o = z_session_getattr(self->handler, "client_address"); *client_address = z_policy_sockaddr_get_sa(o); z_policy_var_unref(o); } if (client_local) { o = z_session_getattr(self->handler, "client_local"); *client_local = z_policy_sockaddr_get_sa(o); z_policy_var_unref(o); } if (client_listen) { o = z_session_getattr(self->handler, "client_listen"); *client_listen = z_policy_dispatch_bind_get_db(o); z_policy_var_unref(o); } if (server_address) { o = z_session_getattr(self->handler, "server_address"); *server_address = z_policy_sockaddr_get_sa(o); z_policy_var_unref(o); } if (server_local) { o = z_session_getattr(self->handler, "server_local"); *server_local = z_policy_sockaddr_get_sa(o); z_policy_var_unref(o); } z_proxy_leave(self); return TRUE; } /** * @param self proxy instance * @param protocol the protocol number (ZD_PROTO_*) is returned here * @param client_address the remote address of the client is returned here * @param client_local the local address of the connection to the client is returned here * @param server_address the remote address of the server is returned here * @param server_local the local address of the connection to the server is returned here * @param client_listen the address of the listener which initiated this session is returned here * * This function is used to query the addresses used to connecting the proxy * to the client and server. The utilized application protocol is also * returned and the listener address which accepted the connection. * * NOTE: this function acquires the thread state associated with @self. **/ gboolean z_proxy_get_addresses(ZProxy *self, guint *protocol, ZSockAddr **client_address, ZSockAddr **client_local, ZSockAddr **server_address, ZSockAddr **server_local, ZDispatchBind **client_listen) { gboolean success; z_policy_thread_acquire(self->thread); success = z_proxy_get_addresses_locked(self, protocol, client_address, client_local, server_address, server_local, client_listen); z_policy_thread_release(self->thread); return success; } /** * z_proxy_set_parent: * @self: ZProxy instance referring to self * @parent: ZProxy instance referring to the parent proxy * * This function is called to change the reference to the parent proxy. * A value of NULL specifies to drop the reference, anything else * removes the earlier reference and assigns a new one. See the * comment on locking at the beginning of this file for more details. **/ gboolean z_proxy_set_parent(ZProxy *self, ZProxy *parent) { ZProxy *old_parent; z_proxy_enter(self); if (parent) { /* establish parent link */ if (!self->parent_proxy) { z_proxy_ref(parent); self->parent_proxy = parent; } else { z_proxy_leave(self); return FALSE; } } else { /* remove parent link */ if (self->parent_proxy) { old_parent = self->parent_proxy; self->parent_proxy = parent; z_proxy_unref(old_parent); } else { z_proxy_leave(self); return FALSE; } } z_proxy_leave(self); return TRUE; } /** * z_proxy_add_child: * @self: ZProxy instance referring to self * @child_proxy: ZProxy instance to be added to the child list * * This function adds the specified ZProxy instance to the child_proxies * linked list. **/ gboolean z_proxy_add_child(ZProxy *self, ZProxy *child_proxy) { z_enter(); if (z_proxy_set_parent(child_proxy, self)) { self->child_proxies = g_list_prepend(self->child_proxies, z_proxy_ref(child_proxy)); z_return(TRUE); } z_return(FALSE); } /** * z_proxy_del_child: * @self: ZProxy instance referring to self * @child_proxy: ZProxy instance to be deleted from the child_proxies list * * This function removes @child_proxy from the child_proxies list in @self. **/ gboolean z_proxy_del_child(ZProxy *self, ZProxy *child_proxy) { z_proxy_enter(self); self->child_proxies = g_list_remove(self->child_proxies, child_proxy); z_proxy_unref(child_proxy); z_proxy_leave(self); return TRUE; } void z_proxy_set_group(ZProxy *self, ZProxyGroup *group) { self->group = z_proxy_group_ref(group); } ZProxyGroup * z_proxy_get_group(ZProxy *self) { return self->group; } /** * z_proxy_add_iface: * @self: ZProxy instance * @iface: exported interface to add * * This function adds an exported function interface callable from * other proxies to the set of supported interface. **/ void z_proxy_add_iface(ZProxy *self, ZProxyIface *iface) { z_object_ref(&iface->super); g_static_mutex_lock(&self->interfaces_lock); self->interfaces = g_list_prepend(self->interfaces, iface); g_static_mutex_unlock(&self->interfaces_lock); } /** * z_proxy_del_iface: * @self: ZProxy instance * @iface: exported interface to delete * * This function deletes the interface specified in @iface from the set of * supported interfaces. * * NOTE: the locking implemented here assumes that the destructor for * z_proxy_iface will not touch interfaces lock again. **/ void z_proxy_del_iface(ZProxy *self, ZProxyIface *iface) { g_static_mutex_lock(&self->interfaces_lock); self->interfaces = g_list_remove(self->interfaces, iface); g_static_mutex_unlock(&self->interfaces_lock); z_object_unref(&iface->super); } /** * z_proxy_find_iface: * @self: ZProxy instance * @compat: search for an interface compatible with this class * * This function iterates on the set of supported interfaces in @self and * returns the first compatible with the class specified in @compat. **/ ZProxyIface * z_proxy_find_iface(ZProxy *self, ZClass *compat) { GList *p; if (!self) return NULL; if (!z_object_is_subclass(Z_CLASS(ZProxyIface), compat)) { /*LOG This message indicates an internal error, please contact your Zorp support for assistance. */ z_proxy_log(self, CORE_ERROR, 3, "Internal error, trying to look up a non-ZProxyIface compatible interface;"); return NULL; } g_static_mutex_lock(&self->interfaces_lock); for (p = self->interfaces; p; p = p->next) { ZObject *obj; ZProxyIface *iface; obj = (ZObject *) p->data; if (z_object_is_compatible(obj, compat)) { iface = (ZProxyIface *) z_object_ref(obj); g_static_mutex_unlock(&self->interfaces_lock); return iface; } } g_static_mutex_unlock(&self->interfaces_lock); return NULL; } void z_proxy_var_register_va(ZProxy *s, ZPolicyDict *dict, const gchar *name, guint flags, va_list args) { guint type = Z_VAR_TYPE(flags); flags = flags & 0x0f; switch (type) { case Z_VAR_TYPE_INT: z_policy_dict_register(dict, Z_VT_INT, name, flags, va_arg(args, gint *), NULL, NULL); break; case Z_VAR_TYPE_INT64: z_policy_dict_register(dict, Z_VT_INT64, name, flags, va_arg(args, gint *), NULL, NULL); break; case Z_VAR_TYPE_STRING: z_policy_dict_register(dict, Z_VT_STRING, name, flags | Z_VF_CONSUME, va_arg(args, GString *), NULL, NULL); break; case Z_VAR_TYPE_OBJECT: z_policy_dict_register(dict, Z_VT_OBJECT, name, flags | Z_VF_CONSUME, va_arg(args, ZPolicyObj **), NULL, NULL); break; case Z_VAR_TYPE_ALIAS: z_policy_dict_register(dict, Z_VT_ALIAS, name, flags, va_arg(args, gchar *), NULL, NULL); break; case Z_VAR_TYPE_OBSOLETE: z_policy_dict_register(dict, Z_VT_ALIAS, name, flags | Z_VF_OBSOLETE, va_arg(args, gchar *), NULL, NULL); break; case Z_VAR_TYPE_METHOD: { gpointer user_data = va_arg(args, gpointer); gpointer method = va_arg(args, gpointer); z_policy_dict_register(dict, Z_VT_METHOD, name, flags, method, user_data, NULL, NULL, NULL); break; } case Z_VAR_TYPE_HASH: z_policy_dict_register(dict, Z_VT_HASH, name, flags | Z_VF_CONSUME, va_arg(args, GHashTable *), NULL, NULL); break; case Z_VAR_TYPE_DIMHASH: z_policy_dict_register(dict, Z_VT_DIMHASH, name, flags | Z_VF_CONSUME, va_arg(args, gpointer), NULL, NULL); break; case Z_VAR_TYPE_CUSTOM: { gpointer value = va_arg(args, gpointer); gpointer get_value = va_arg(args, gpointer); gpointer set_value = va_arg(args, gpointer); gpointer free_value = va_arg(args, gpointer); z_policy_dict_register(dict, Z_VT_CUSTOM, name, flags, value, get_value, set_value, free_value, s, NULL, // user_data, user_data_free NULL, // end of CUSTOM args NULL); break; } default: g_assert(0); break; } } void z_proxy_var_new(ZProxy *self, const gchar *name, guint flags, ...) { va_list(args); va_start(args, flags); z_proxy_var_register_va(self, self->dict, name, flags, args); va_end(args); } /** * FIXME: we may want to add functions to manipulate self->endpoints and * update self->py_endpoints whenever self->endpoints changes. The * implementation here basically assumes that whenever self->endpoints * changes the Python layer queries proxy.client_stream or * proxy.server_stream. If this is not the case the Python layer may use a * stale stream. **/ static ZPolicyObj * z_proxy_query_stream(ZProxy *self, gchar *name, gpointer value G_GNUC_UNUSED) { ZPolicyObj *res; gint side; z_proxy_enter(self); if (strcmp(name, "client_stream") == 0) { side = EP_CLIENT; } else if (strcmp(name, "server_stream") == 0) { side = EP_SERVER; } else { g_assert_not_reached(); } res = self->py_endpoints[side]; if (!res) { /* no stream yet in cache */ if (self->endpoints[side]) { /* but there is one in C side */ self->py_endpoints[side] = res = z_policy_stream_new(self->endpoints[side]); } else { res = z_policy_none; } } else if (((ZPolicyStream *) res)->stream != self->endpoints[side]) { /* the cache is out of sync */ z_stream_unref(((ZPolicyStream *)res)->stream); z_stream_ref(self->endpoints[side]); ((ZPolicyStream *)res)->stream = self->endpoints[side]; } z_policy_var_ref(res); z_proxy_leave(self); return res; } /* methods for the ZProxy class */ /** * z_proxy_config_method: * @self: ZProxy instance * * This function is referenced as the default config method for the ZProxy * class. It calls the "config" method in the policy. * Returns FALSE upon failure, and TRUE otherwise. **/ gboolean z_proxy_config_method(ZProxy *self) { z_policy_dict_register(self->dict, Z_VT_INT8, "client_remote_tos", Z_VF_RW, &self->channel_props[EP_CLIENT].tos[EP_DIR_IN], NULL); z_policy_dict_register(self->dict, Z_VT_INT8, "client_local_tos", Z_VF_RW, &self->channel_props[EP_CLIENT].tos[EP_DIR_OUT], NULL); z_policy_dict_register(self->dict, Z_VT_INT8, "server_remote_tos", Z_VF_RW, &self->channel_props[EP_SERVER].tos[EP_DIR_IN], NULL); z_policy_dict_register(self->dict, Z_VT_INT8, "server_local_tos", Z_VF_RW, &self->channel_props[EP_SERVER].tos[EP_DIR_OUT], NULL); z_proxy_var_new(self, "language", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, self->language); z_proxy_var_new(self, "client_stream", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, z_proxy_query_stream, NULL, NULL); z_proxy_var_new(self, "server_stream", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, z_proxy_query_stream, NULL, NULL); z_proxy_ssl_register_vars(self); return z_proxy_policy_config(self); } /** * z_proxy_startup_method: * @self: ZProxy instance * * This function is referenced as the default startup method for the ZProxy * class. It calls the "startup" method in the policy. * Returns FALSE upon failure, and TRUE otherwise. **/ gboolean z_proxy_startup_method(ZProxy *self) { return z_proxy_policy_startup(self); } /** * z_proxy_main_method: * @self: ZProxy instance * * This function is referenced as the default main method for the ZProxy * class. Currently it does nothing and should be overriden in descendant * classes. **/ void z_proxy_main_method(ZProxy *self G_GNUC_UNUSED) { ; } /** * z_proxy_shutdown_method: * @self: ZProxy instance * * This function is referenced as the default shutdown method for the ZProxy * class. It calls the "shutdown" method in the policy. * Returns FALSE upon failure, and TRUE otherwise. **/ void z_proxy_shutdown_method(ZProxy *self) { z_proxy_policy_shutdown(self); } /** * z_proxy_destroy_method: * @self: proxy instance * * This function is called from proxy implementation when the proxy is to * exit. Frees up associated resources, closes streams, etc. Note that the * ZProxy instance is not freed immediately as the reference from Python and * the caller still exists. z_proxy_destroy() ensures however that circular * references are resolved so the proxy will be freed as soon as those * references are dropped. **/ void z_proxy_destroy_method(ZProxy *self) { int i; ZPolicyObj *handler; ZPolicyThread *thread; ZPolicyDict *dict; GList *ifaces, *p; z_proxy_enter(self); z_proxy_policy_destroy(self); /* this also removes the link to parent */ z_proxy_set_parent(self, NULL); while (self->child_proxies) { z_proxy_del_child(self, (ZProxy *) self->child_proxies->data); } g_static_mutex_lock(&self->interfaces_lock); ifaces = self->interfaces; self->interfaces = NULL; g_static_mutex_unlock(&self->interfaces_lock); while (ifaces) { z_object_unref((ZObject *) ifaces->data); p = ifaces; ifaces = ifaces->next; g_list_free_1(p); } z_proxy_unregister(self); thread = self->thread; if (z_proxy_get_state(self) > ZPS_THREAD_STARTED) { for (i = EP_CLIENT; i <= EP_SERVER; i++) { z_policy_thread_acquire(thread); z_policy_var_unref(self->py_endpoints[i]); z_policy_thread_release(thread); if (self->endpoints[i]) { z_stream_shutdown(self->endpoints[i], SHUT_RDWR, NULL); z_stream_close(self->endpoints[i], NULL); z_stream_unref(self->endpoints[i]); self->endpoints[i] = NULL; } } z_policy_thread_acquire(thread); self->thread = NULL; z_proxy_ssl_free_vars(self); dict = self->dict; self->dict = NULL; z_policy_dict_unwrap(dict, self->handler); z_policy_dict_destroy(dict); handler = self->handler; self->handler = NULL; z_policy_var_unref(handler); z_policy_thread_release(thread); } else { self->thread = NULL; } z_policy_thread_destroy(thread); z_proxy_leave(self); } /** * z_proxy_run_method: * @self: ZProxy instance * * This function is referenced as the default run method for the ZProxy * class. It is started by the proxy specific thread and calls the * appropriate policy functions (config, startup), then continues by * calling z_proxy_main(). **/ void z_proxy_run(ZProxy *self) { z_proxy_enter(self); if (z_proxy_config(self) && z_proxy_startup(self) && z_proxy_ssl_init_stream(self, EP_CLIENT)) { z_proxy_propagate_channel_props(self); z_proxy_main(self); } z_proxy_shutdown(self); z_proxy_destroy(self); z_proxy_leave(self); } /** * z_proxy_thread_func: * @s: ZProxy instance as a general pointer * * This is the default thread function for proxies. The thread is started * in z_proxy_start(). **/ static gpointer z_proxy_thread_func(gpointer s) { ZProxy *self = Z_CAST(s, ZProxy); self->proxy_thread = z_thread_self(); z_proxy_set_state(self, ZPS_THREAD_STARTED); z_proxy_run(self); z_proxy_unref(self); return NULL; } /** * z_proxy_threaded_start: * @self: ZProxy instance * * Starts the proxy by creating the new proxy thread. This function * is usually called by proxy constructors. **/ gboolean z_proxy_threaded_start(ZProxy *self, ZProxyGroup *proxy_group) { z_proxy_set_group(self, proxy_group); z_proxy_ref(self); if (!z_thread_new(self->session_id, z_proxy_thread_func, self)) { /*LOG This message indicates that Zorp was unable to create a new thread for the new proxy instance. It is likely that Zorp reached a thread limit, or not enough resource is available. */ z_proxy_log(self, CORE_ERROR, 2, "Error creating proxy thread;"); z_proxy_destroy(self); z_proxy_unref(self); return FALSE; } return TRUE; } gboolean z_proxy_nonblocking_start(ZProxy *self, ZProxyGroup *proxy_group) { gboolean success; z_proxy_set_group(self, proxy_group); success = z_proxy_config(self) && z_proxy_startup(self) && z_proxy_ssl_init_stream_nonblocking(self, EP_CLIENT); return success; } void z_proxy_nonblocking_stop(ZProxy *self) { z_proxy_nonblocking_deinit(self); z_proxy_shutdown(self); z_proxy_destroy(self); z_proxy_group_stop_session(self->group, self); } /** * z_proxy_wakeup: * @self: ZProxy instance * * This function should try its best to wake-up the specified ZProxy * instance from sleeping, to check for example the stop-request flag. * * It currently checks whether the proxy is a non-blocking one, and wakes up * the associated poll loop. * * Proxies might override this function to provide additional wakeup methods. * * NOTE: this runs in a separate thread **/ void z_proxy_wakeup_method(ZProxy *self) { if ((self->flags & ZPF_NONBLOCKING) != 0) z_proxy_group_wakeup(self->group); } /** * z_proxy_loop_iteration: * @s: the proxy instance * * This function is to be called by proxies in their main loop. Whenever * this function returns FALSE the proxy should finish its processing and exit. * * It currently calls propagate_channel_props and checks the ZPF_STOP_REQUEST flag. * * Returns: TRUE if the proxy can continue, FALSE if it has to be stopped * */ gboolean z_proxy_loop_iteration(ZProxy *s) { z_proxy_propagate_channel_props(s); if (z_proxy_stop_requested(s)) { /*LOG A stop request arrived to the proxy. It has to be stopped. */ z_proxy_log(s, CORE_INFO, 2, "User initiated proxy termination request received;"); return FALSE; } else { return TRUE; } } /** * z_proxy_new: * @proxy_class: proxy class to instantiate * @params: ZProxyParams containing ZProxy parameters * * This function is to be called from proxy constructors to initialize * common fields in the ZProxy struct. * * NOTE: unlike in previous versions, z_proxy_new is called with the Python * interpreter unlocked, thus it must grab the interpreter lock to create * thread specific Python state. * **/ ZProxy * z_proxy_new(ZClass *proxy_class, ZProxyParams *params) { ZProxy *self; ZProxyIface *iface; ZPolicyThread *policy_thread; z_enter(); self = Z_NEW_COMPAT(proxy_class, ZProxy); if (params->client) { self->endpoints[EP_CLIENT] = params->client; z_stream_ref(params->client); } g_strlcpy(self->session_id, params->session_id, sizeof(self->session_id)); self->language = g_string_new("en"); self->dict = z_policy_dict_new(); iface = (ZProxyIface *) z_proxy_basic_iface_new(Z_CLASS(ZProxyBasicIface), self); z_proxy_add_iface(self, iface); z_object_unref(&iface->super); z_python_lock(); z_policy_dict_wrap(self->dict, params->handler); self->handler = params->handler; z_policy_var_ref(params->handler); policy_thread = z_policy_thread_self(); self->thread = z_policy_thread_new(policy_thread ? z_policy_thread_get_policy(policy_thread) : current_policy); z_python_unlock(); z_proxy_register(self); z_proxy_ssl_config_defaults(self); z_proxy_add_child(params->parent, self); z_return(self); } /** * z_proxy_free_method: * @self: proxy instance * * Called when the proxy object is finally to be freed (when the Python layer * releases its reference). Calls the proxy specific free function and * frees self **/ void z_proxy_free_method(ZObject *s) { ZProxy *self = Z_CAST(s, ZProxy); z_enter(); z_proxy_log(self, CORE_DEBUG, 7, "Freeing ZProxy instance;"); z_proxy_group_unref(self->group); z_object_free_method(s); z_leave(); } static ZProxyFuncs z_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), z_proxy_free_method, }, .config = z_proxy_config_method, .startup = z_proxy_startup_method, .main = z_proxy_main_method, .shutdown = z_proxy_shutdown_method, .destroy = z_proxy_destroy_method, .nonblocking_init = NULL, .nonblocking_deinit = NULL, .wakeup = z_proxy_wakeup_method }; Z_CLASS_DEF(ZProxy, ZObject, z_proxy_funcs); /* ZProxyIface */ /** * z_proxy_iface: * @class: derived class description * @proxy: proxy instance to be associated with this interface * * Constructor for ZProxyIface objects and derivates. A ZProxyIface * class encapsulates a function interface which permits inter-proxy * communication. **/ ZProxyIface * z_proxy_iface_new(ZClass *class, ZProxy *proxy) { ZProxyIface *self; self = Z_NEW_COMPAT(class, ZProxyIface); self->owner = z_proxy_ref(proxy); return self; } /** * z_proxy_iface_free_method: * @s: ZProxyIface instance passed as a ZObject pointer * * Destructor for ZProxyIface objects, frees associated references. **/ void z_proxy_iface_free_method(ZObject *s) { ZProxyIface *self = Z_CAST(s, ZProxyIface); z_proxy_unref(self->owner); self->owner = NULL; z_object_free_method(s); } ZObjectFuncs z_proxy_iface_funcs = { Z_FUNCS_COUNT(ZObject), z_proxy_iface_free_method, }; Z_CLASS_DEF(ZProxyIface, ZObject, z_proxy_iface_funcs); /* ZProxyBasicIface */ static gboolean z_proxy_basic_iface_get_var_method(ZProxyBasicIface *self, const gchar *var_name, gchar **value) { ZPolicyObj *value_obj, *value_str; ZProxy *owner = self->owner; gboolean success = FALSE; z_policy_lock(owner->thread); value_obj = z_policy_getattr(owner->handler, (gchar *) var_name); if (!value_obj) goto exit; value_str = z_policy_var_str(value_obj); g_assert(z_policy_str_check(value_str)); *value = g_strdup(z_policy_str_as_string(value_str)); z_policy_var_unref(value_obj); z_policy_var_unref(value_str); success = TRUE; exit: z_policy_unlock(owner->thread); return success; } static gboolean z_proxy_basic_iface_set_var_method(ZProxyBasicIface *self G_GNUC_UNUSED, const gchar *var_name G_GNUC_UNUSED, gchar *value G_GNUC_UNUSED) { return FALSE; } /** * z_proxy_basic_iface_new: * @class: class description * @proxy: associated proxy * * Constructor for ZProxyBasicIface class, derived from ZProxyIface. **/ ZProxyBasicIface * z_proxy_basic_iface_new(ZClass *class, ZProxy *proxy) { ZProxyBasicIface *self; self = (ZProxyBasicIface *) z_proxy_iface_new(class, proxy); return self; } ZProxyBasicIfaceFuncs z_proxy_basic_iface_funcs = { { Z_FUNCS_COUNT(ZObject), NULL }, .get_var = z_proxy_basic_iface_get_var_method, .set_var = z_proxy_basic_iface_set_var_method, }; Z_CLASS_DEF(ZProxyBasicIface, ZProxyIface, z_proxy_basic_iface_funcs); ZProxyStackIfaceFuncs z_proxy_stack_iface_funcs = { { Z_FUNCS_COUNT(ZObject), NULL }, .set_verdict = NULL, .get_content_hint = NULL, .set_content_hint = NULL }; Z_CLASS_DEF(ZProxyStackIface, ZProxyIface, z_proxy_stack_iface_funcs); ZProxyHostIfaceFuncs z_proxy_host_iface_funcs = { { Z_FUNCS_COUNT(ZObject), .free_fn = NULL }, .check_name = NULL, }; Z_CLASS_DEF(ZProxyHostIface, ZProxyIface, z_proxy_host_iface_funcs); zorp-3.9.5/lib/proxygroup.c000066400000000000000000000153561172670260400157110ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #include #include #include struct _ZProxyGroup { ZRefCount ref_cnt; GStaticMutex lock; gboolean thread_started; gboolean orphaned; GAsyncQueue *nonblocking_start_queue; GList *nonblocking_proxies; ZPoll *poll; guint sessions; guint max_sessions; }; static gpointer z_proxy_group_thread_func(gpointer s) { ZProxyGroup *self = (ZProxyGroup *) s; z_enter(); g_static_mutex_lock(&self->lock); self->poll = z_poll_new(); g_static_mutex_unlock(&self->lock); while (!self->orphaned || self->sessions > 0) { z_proxy_group_iteration(self); } z_proxy_group_unref(self); z_leave(); return NULL; } static gboolean z_proxy_group_start_thread(ZProxyGroup *self) { z_enter(); g_static_mutex_lock(&self->lock); if (!self->thread_started) { self->nonblocking_start_queue = g_async_queue_new(); self->thread_started = TRUE; g_static_mutex_unlock(&self->lock); if (!z_thread_new("group", z_proxy_group_thread_func, z_proxy_group_ref(self))) { z_proxy_group_unref(self); z_leave(); return FALSE; } } else { g_static_mutex_unlock(&self->lock); } z_leave(); return TRUE; } gboolean z_proxy_group_start_session(ZProxyGroup *self, ZProxy *proxy) { gboolean started; z_enter(); g_static_mutex_lock(&self->lock); if (self->sessions >= self->max_sessions) { g_static_mutex_unlock(&self->lock); z_leave(); return FALSE; } self->sessions++; g_static_mutex_unlock(&self->lock); if (proxy->flags & ZPF_NONBLOCKING) { if (!z_proxy_group_start_thread(self)) { z_leave(); return FALSE; } g_async_queue_push(self->nonblocking_start_queue, z_proxy_ref(proxy)); g_static_mutex_lock(&self->lock); if (self->poll) z_poll_wakeup(self->poll); g_static_mutex_unlock(&self->lock); z_leave(); return TRUE; } else { Py_BEGIN_ALLOW_THREADS; started = z_proxy_threaded_start(proxy, self); Py_END_ALLOW_THREADS; if (!started) { z_log(NULL, CORE_ERROR, 1, "Error starting proxy; module='%s'", proxy->super.isa->name); z_leave(); return FALSE; } z_policy_thread_ready(proxy->thread); z_leave(); return TRUE; } } void z_proxy_group_stop_session(ZProxyGroup *self, ZProxy *proxy) { z_enter(); if (proxy->flags & ZPF_NONBLOCKING) { GList *l; /* NOTE: nonblocking proxies run in the same thread, thus this * function is always called from the ZProxyGroup thread, no locking * is necessary */ /* FIXME: use a better list deletion algorithm (like embed a list * header to ZProxy and use an O(1) deletion */ l = g_list_find(self->nonblocking_proxies, proxy); if (l) { self->nonblocking_proxies = g_list_delete_link(self->nonblocking_proxies, l); z_proxy_unref(proxy); } } g_static_mutex_lock(&self->lock); self->sessions--; g_static_mutex_unlock(&self->lock); z_leave(); } GMainContext * z_proxy_group_get_context(ZProxyGroup *self) { if (self->poll) return z_poll_get_context(self->poll); return NULL; } ZPoll * z_proxy_group_get_poll(ZProxyGroup *self) { return self->poll; } gboolean z_proxy_group_iteration(ZProxyGroup *self) { ZProxy *proxy; GList *p; gboolean res = FALSE; z_enter(); while ((proxy = g_async_queue_try_pop(self->nonblocking_start_queue))) { z_policy_thread_ready(proxy->thread); if (!z_proxy_nonblocking_start(proxy, self)) { z_proxy_nonblocking_stop(proxy); z_proxy_unref(proxy); } else { self->nonblocking_proxies = g_list_prepend(self->nonblocking_proxies, proxy); } } for (p = self->nonblocking_proxies; p; p = p->next) { if (!z_proxy_loop_iteration((ZProxy *) p->data)) { z_proxy_nonblocking_stop((ZProxy *) p->data); } } if (!self->orphaned || self->sessions > 0) res = z_poll_iter_timeout(self->poll, -1); z_leave(); return res; } void z_proxy_group_orphan(ZProxyGroup *self) { self->orphaned = TRUE; if (self->poll) z_poll_wakeup(self->poll); z_proxy_group_unref(self); } /** * z_proxy_group_wakeup: * * NOTE: runs from a different thread, but with a reference held to self **/ void z_proxy_group_wakeup(ZProxyGroup *self) { z_poll_wakeup(self->poll); } ZProxyGroup * z_proxy_group_new(gint max_sessions) { ZProxyGroup *self = g_new0(ZProxyGroup, 1); z_refcount_set(&self->ref_cnt, 1); if (max_sessions) self->max_sessions = max_sessions; else self->max_sessions = 1; return self; } ZProxyGroup * z_proxy_group_ref(ZProxyGroup *self) { z_refcount_inc(&self->ref_cnt); return self; } void z_proxy_group_unref(ZProxyGroup *self) { if (self && z_refcount_dec(&self->ref_cnt)) { if (self->nonblocking_start_queue) { ZProxy *proxy; while ((proxy = g_async_queue_try_pop(self->nonblocking_start_queue))) { z_proxy_unref(proxy); } g_async_queue_unref(self->nonblocking_start_queue); } while (self->nonblocking_proxies) { z_proxy_unref((ZProxy *) self->nonblocking_proxies->data); self->nonblocking_proxies = g_list_delete_link(self->nonblocking_proxies, self->nonblocking_proxies); } if (self->poll) z_poll_unref(self->poll); g_free(self); } } zorp-3.9.5/lib/proxyssl.c000066400000000000000000002056441172670260400153570ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Panther * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include /** * Create a new SSL handshake object. * * @param proxy the proxy instance * @param stream the stream we're to handshake on * @param side the side the handshake is to be made on * (determines the SSL parameters to be used) * * This function creates a handshake object with the parameters passed in. * The object returned is not reference-counted, but 'garbage-collected' * when freeing destroying @proxy. * * @return the new handshake object (cannot return NULL) */ ZProxySSLHandshake * z_proxy_ssl_handshake_new(ZProxy * proxy, ZStream *stream, gint side) { ZProxySSLHandshake *self; g_assert(proxy != NULL); g_assert(stream != NULL); z_proxy_enter(proxy); self = g_new0(ZProxySSLHandshake, 1); self->proxy = z_proxy_ref(proxy); self->stream = z_stream_ref(stream); self->side = side; self->session = NULL; self->timeout = NULL; /* append the handshake to the list of handshakes done by this proxy */ proxy->ssl_opts.handshakes = g_list_append(proxy->ssl_opts.handshakes, self); z_proxy_return(proxy, self); } /** * Destroy a handshake object. * * @param self the handshake object to be destroyed * * Destroys a handshake object by freeing/dereferencing all associated objects * and then freeing the structure. */ static void z_proxy_ssl_handshake_destroy(ZProxySSLHandshake *self) { ZProxy *p = self->proxy; z_proxy_enter(p); if (self->timeout) { g_source_destroy(self->timeout); g_source_unref(self->timeout); } if (self->session) z_ssl_session_unref(self->session); if (self->ssl_context) SSL_CTX_free(self->ssl_context); z_stream_unref(self->stream); g_free(self); z_proxy_leave(p); z_proxy_unref(p); } /** * Set the handshake completion callback for a handshake. * * @param self the handshake object * @param cb the callback function * @param user_data user data passed to the callback * @param user_data_notify destroy notify callback used to free @user_data * * This function sets the completion callback and its associated arguments to * be used when the SSL handshake has been completed. Since @user_data might * be refcounted we always use @user_data_notify when freeing @user_data. */ static void z_proxy_ssl_handshake_set_callback(ZProxySSLHandshake *self, ZProxySSLCallbackFunc cb, gpointer user_data, GDestroyNotify user_data_notify) { self->completion_cb = cb; self->completion_user_data = user_data; self->completion_user_data_notify = user_data_notify; } /** * Call the SSL handshake completion callback *once*. * * @param self the handshake object * * Calls the completion callback set by z_proxy_ssl_handshake_set_callback(). * * After the call it clears all info regarding the callback so that it * won't be called again. */ static void z_proxy_ssl_handshake_call_callback(ZProxySSLHandshake *self) { ZProxySSLCallbackFunc callback; gpointer user_data; GDestroyNotify user_data_notify; z_enter(); callback = self->completion_cb; user_data = self->completion_user_data; user_data_notify = self->completion_user_data_notify; /* make sure we're resetting these so that we never call the * callback more than once */ self->completion_cb = NULL; self->completion_user_data = NULL; self->completion_user_data_notify = NULL; /* since the completion callback might lead to the destruction of * the handshake object itself we must not dereference self after * calling the callback! */ if (callback) (callback)(self, user_data); if (user_data && user_data_notify) (user_data_notify)(user_data); z_leave(); } static void z_proxy_ssl_handshake_set_error(ZProxySSLHandshake *self, gint ssl_err) { self->ssl_err = ssl_err; z_ssl_get_error_str(self->ssl_err_str, sizeof(self->ssl_err_str)); } static gint z_proxy_ssl_handshake_get_error(ZProxySSLHandshake *self) { return self->ssl_err; } static const gchar * z_proxy_ssl_handshake_get_error_str(ZProxySSLHandshake *self) { return self->ssl_err_str; } /** * Set default values of SSL attributes. * * @param self the proxy being initialized * * The function initializes all SSL related members of the proxy instance. */ void z_proxy_ssl_config_defaults(ZProxy *self) { int i; self->ssl_opts.handshake_timeout = 30000; self->ssl_opts.handshake_seq = PROXY_SSL_HS_CLIENT_SERVER; self->ssl_opts.permit_invalid_certificates = FALSE; self->ssl_opts.permit_missing_crl = TRUE; self->ssl_opts.verify_type[EP_SERVER] = PROXY_SSL_VERIFY_REQUIRED_TRUSTED; self->ssl_opts.verify_type[EP_CLIENT] = PROXY_SSL_VERIFY_REQUIRED_TRUSTED; self->ssl_opts.verify_depth[EP_SERVER] = 4; self->ssl_opts.verify_depth[EP_CLIENT] = 4; self->ssl_opts.verify_ca_directory[EP_CLIENT] = g_string_new(""); self->ssl_opts.verify_ca_directory[EP_SERVER] = g_string_new(""); self->ssl_opts.verify_crl_directory[EP_CLIENT] = g_string_new(""); self->ssl_opts.verify_crl_directory[EP_SERVER] = g_string_new(""); for (i = 0; i < EP_MAX; i++) { self->ssl_opts.local_ca_list[i] = sk_X509_new_null(); self->ssl_opts.local_crl_list[i] = sk_X509_CRL_new_null(); self->ssl_opts.handshake_hash[i] = g_hash_table_new(g_str_hash, g_str_equal); } self->ssl_opts.server_peer_ca_list = sk_X509_NAME_new_null(); self->ssl_opts.ssl_method[EP_CLIENT] = g_string_new("SSLv23"); self->ssl_opts.ssl_method[EP_SERVER] = g_string_new("SSLv23"); self->ssl_opts.ssl_cipher[EP_CLIENT] = g_string_new("ALL:!aNULL:@STRENGTH"); self->ssl_opts.ssl_cipher[EP_SERVER] = g_string_new("ALL:!aNULL:@STRENGTH"); self->ssl_opts.disable_proto_sslv2[EP_CLIENT] = self->ssl_opts.disable_proto_sslv2[EP_SERVER] = TRUE; self->ssl_opts.local_privkey_passphrase[EP_CLIENT] = g_string_new(""); self->ssl_opts.local_privkey_passphrase[EP_SERVER] = g_string_new(""); self->ssl_opts.server_check_subject = TRUE; self->ssl_opts.dict = z_policy_dict_new(); z_python_lock(); z_policy_dict_ref(self->ssl_opts.dict); self->ssl_opts.ssl_struct = z_policy_struct_new(self->ssl_opts.dict, Z_PST_SHARED); z_python_unlock(); g_assert(self->ssl_opts.ssl_struct != NULL); z_policy_var_ref(self->ssl_opts.ssl_struct); z_policy_dict_register(self->dict, Z_VT_OBJECT, "ssl", Z_VF_READ | Z_VF_CFG_READ | Z_VF_LITERAL | Z_VF_CONSUME, self->ssl_opts.ssl_struct); } /** * Export SSL related attributes to Python. * * @param self the proxy being initialized * * This function registers all exported SSL attributes with the Python * interpreter. */ void z_proxy_ssl_register_vars(ZProxy *self) { ZPolicyDict *dict = self->ssl_opts.dict; /* enable ssl */ z_policy_dict_register(dict, Z_VT_INT, "client_connection_security", Z_VF_READ | Z_VF_CFG_RW, &self->ssl_opts.security[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_INT, "server_connection_security", Z_VF_READ | Z_VF_CFG_RW, &self->ssl_opts.security[EP_SERVER]); /* common members */ z_policy_dict_register(dict, Z_VT_INT, "handshake_timeout", Z_VF_READ | Z_VF_CFG_RW, &self->ssl_opts.handshake_timeout); z_policy_dict_register(dict, Z_VT_INT, "handshake_seq", Z_VF_READ | Z_VF_CFG_RW, &self->ssl_opts.handshake_seq); z_policy_dict_register(dict, Z_VT_INT, "permit_invalid_certificates", Z_VF_RW | Z_VF_CFG_RW, &self->ssl_opts.permit_invalid_certificates); z_policy_dict_register(dict, Z_VT_INT, "permit_missing_crl", Z_VF_RW | Z_VF_CFG_RW, &self->ssl_opts.permit_missing_crl); /* client side */ z_policy_dict_register(dict, Z_VT_HASH, "client_handshake", Z_VF_READ | Z_VF_CFG_READ | Z_VF_CONSUME, self->ssl_opts.handshake_hash[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_INT, "client_verify_type", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.verify_type[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_INT, "client_max_verify_depth", Z_VF_READ | Z_VF_CFG_RW, &self->ssl_opts.verify_depth[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_ALIAS, "client_verify_depth", Z_VF_READ | Z_VF_CFG_RW, "client_max_verify_depth"); z_policy_dict_register(dict, Z_VT_CUSTOM, "client_local_privatekey", Z_VF_RW | Z_VF_CFG_RW, &self->ssl_opts.local_privkey[EP_CLIENT], z_py_ssl_privkey_get, z_py_ssl_privkey_set, z_py_ssl_privkey_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_STRING, "client_local_privatekey_passphrase", Z_VF_RW | Z_VF_CFG_RW | Z_VF_CONSUME, self->ssl_opts.local_privkey_passphrase[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_CUSTOM, "client_local_certificate", Z_VF_RW | Z_VF_CFG_RW, &self->ssl_opts.local_cert[EP_CLIENT], z_py_ssl_certificate_get, z_py_ssl_certificate_set, z_py_ssl_certificate_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "client_peer_certificate", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.peer_cert[EP_CLIENT], z_py_ssl_certificate_get, NULL, z_py_ssl_certificate_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "client_local_ca_list", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.local_ca_list[EP_CLIENT], z_py_ssl_cert_list_get, NULL, z_py_ssl_cert_list_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "client_local_crl_list", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.local_crl_list[EP_CLIENT], z_py_ssl_crl_list_get, NULL, z_py_ssl_crl_list_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_STRING, "client_verify_ca_directory", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.verify_ca_directory[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_STRING, "client_verify_crl_directory", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.verify_crl_directory[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_STRING, "client_ssl_method", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.ssl_method[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_INT, "client_disable_proto_sslv2", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.disable_proto_sslv2[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_INT, "client_disable_proto_sslv3", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.disable_proto_sslv3[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_INT, "client_disable_proto_tlsv1", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.disable_proto_tlsv1[EP_CLIENT]); z_policy_dict_register(dict, Z_VT_STRING, "client_ssl_cipher", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.ssl_cipher[EP_CLIENT]); /* server side */ z_policy_dict_register(dict, Z_VT_HASH, "server_handshake", Z_VF_READ | Z_VF_CFG_READ | Z_VF_CONSUME, self->ssl_opts.handshake_hash[EP_SERVER]); z_policy_dict_register(dict, Z_VT_INT, "server_verify_type", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.verify_type[EP_SERVER]); z_policy_dict_register(dict, Z_VT_INT, "server_max_verify_depth", Z_VF_READ | Z_VF_CFG_RW, &self->ssl_opts.verify_depth[EP_SERVER]); z_policy_dict_register(dict, Z_VT_ALIAS, "server_verify_depth", Z_VF_READ | Z_VF_CFG_RW, "server_max_verify_depth"); z_policy_dict_register(dict, Z_VT_CUSTOM, "server_local_privatekey", Z_VF_RW | Z_VF_CFG_RW, &self->ssl_opts.local_privkey[EP_SERVER], z_py_ssl_privkey_get, z_py_ssl_privkey_set, z_py_ssl_privkey_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_STRING, "server_local_privatekey_passphrase", Z_VF_RW | Z_VF_CFG_RW | Z_VF_CONSUME, self->ssl_opts.local_privkey_passphrase[EP_SERVER]); z_policy_dict_register(dict, Z_VT_CUSTOM, "server_local_certificate", Z_VF_RW | Z_VF_CFG_RW, &self->ssl_opts.local_cert[EP_SERVER], z_py_ssl_certificate_get, z_py_ssl_certificate_set, z_py_ssl_certificate_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "server_peer_certificate", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.peer_cert[EP_SERVER], z_py_ssl_certificate_get, NULL, z_py_ssl_certificate_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "server_local_ca_list", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.local_ca_list[EP_SERVER], z_py_ssl_cert_list_get, NULL, z_py_ssl_cert_list_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "server_peer_ca_list", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.server_peer_ca_list, z_py_ssl_cert_name_list_get, NULL, z_py_ssl_cert_name_list_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_CUSTOM, "server_local_crl_list", Z_VF_READ | Z_VF_CFG_READ, &self->ssl_opts.local_crl_list[EP_SERVER], z_py_ssl_crl_list_get, NULL, z_py_ssl_crl_list_free, self, NULL, /* user_data, user_data_free */ NULL, /* end of CUSTOM args */ NULL); z_policy_dict_register(dict, Z_VT_STRING, "server_verify_ca_directory", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.verify_ca_directory[EP_SERVER]); z_policy_dict_register(dict, Z_VT_STRING, "server_verify_crl_directory", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.verify_crl_directory[EP_SERVER]); z_policy_dict_register(dict, Z_VT_STRING, "server_ssl_method", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.ssl_method[EP_SERVER]); z_policy_dict_register(dict, Z_VT_INT, "server_disable_proto_sslv2", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.disable_proto_sslv2[EP_SERVER]); z_policy_dict_register(dict, Z_VT_INT, "server_disable_proto_sslv3", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.disable_proto_sslv3[EP_SERVER]); z_policy_dict_register(dict, Z_VT_INT, "server_disable_proto_tlsv1", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.disable_proto_tlsv1[EP_SERVER]); z_policy_dict_register(dict, Z_VT_STRING, "server_ssl_cipher", Z_VF_READ | Z_VF_CFG_WRITE | Z_VF_CONSUME, self->ssl_opts.ssl_cipher[EP_SERVER]); z_policy_dict_register(dict, Z_VT_INT, "server_check_subject", Z_VF_READ | Z_VF_CFG_WRITE, &self->ssl_opts.server_check_subject); } /** * Free SSL related attributes of the Proxy instance. * * @param self the proxy instance being destroyed * * Drop all references to other objects, this is being called when the proxy is * being shut down. */ void z_proxy_ssl_free_vars(ZProxy *self) { gint ep; GList *p; z_enter(); g_assert(self->ssl_opts.dict != NULL); g_assert(self->ssl_opts.ssl_struct != NULL); z_policy_var_unref(self->ssl_opts.ssl_struct); self->ssl_opts.ssl_struct = NULL; z_policy_dict_unref(self->ssl_opts.dict); self->ssl_opts.dict = NULL; for (ep = EP_CLIENT; ep < EP_MAX; ep++) { if (self->ssl_opts.ssl_sessions[ep]) { z_ssl_session_unref(self->ssl_opts.ssl_sessions[ep]); self->ssl_opts.ssl_sessions[ep] = NULL; } } for (p = self->ssl_opts.handshakes; p; p = p->next) { z_proxy_ssl_handshake_destroy((ZProxySSLHandshake *) p->data); } g_list_free(self->ssl_opts.handshakes); self->ssl_opts.handshakes = NULL; z_leave(); } /** * Register SSL host interface if necessary. * * @param self the proxy instance for which the SSL host interface is to be registered * * This functions checks the policy settings and registers the SSL host * interface used for certificate subject verification if necessary. */ static void z_proxy_ssl_register_host_iface(ZProxy *self) { z_proxy_enter(self); if (self->ssl_opts.security[EP_SERVER] > PROXY_SSL_SEC_NONE && self->ssl_opts.ssl_sessions[EP_SERVER] && self->ssl_opts.server_check_subject && (self->ssl_opts.verify_type[EP_SERVER] == PROXY_SSL_VERIFY_OPTIONAL_TRUSTED || self->ssl_opts.verify_type[EP_SERVER] == PROXY_SSL_VERIFY_REQUIRED_TRUSTED)) { ZProxyIface *iface; iface = z_proxy_ssl_host_iface_new(self); z_proxy_add_iface(self, iface); z_object_unref(&iface->super); } z_proxy_leave(self); } /** * Check if an SSL policy callback function exists. * * @param self the proxy instance * @param ndx the side we're doing the SSL handshake on * @param name name of the callback * * This function checks if an SSL callback function exists with the given name. * * @return TRUE if a callback called "name" exists, FALSE otherwise */ static inline gboolean z_proxy_ssl_callback_exists(ZProxy *self, gint ndx, gchar *name) { return !!g_hash_table_lookup(self->ssl_opts.handshake_hash[ndx], name); } /** * Call an SSL policy callback function. * * @param self the proxy instance * @param ndx the side we're doing the SSL handshake on * @param name name of the callback * @param args arguments to be passed to the callback * @param[out] retval the return value of the callback * * This function evaluates the policy settings for the named callback. * In case a Python callback function is configured in the policy, * it calls the function with the arguments passed in args. * * @return TRUE if evaluating the policy settings was successful, FALSE otherwise */ static gboolean z_proxy_ssl_callback(ZProxy *self, gint ndx, gchar *name, ZPolicyObj *args, guint *retval) { ZPolicyObj *tuple, *cb, *res; gboolean rc = FALSE; guint type; z_proxy_enter(self); tuple = g_hash_table_lookup(self->ssl_opts.handshake_hash[ndx], name); if (!tuple) { *retval = PROXY_SSL_HS_ACCEPT; z_policy_var_unref(args); z_proxy_return(self, TRUE); } if (!z_policy_var_parse(tuple, "(iO)", &type, &cb)) { z_policy_var_unref(args); z_proxy_log(self, CORE_POLICY, 1, "Handshake hash item is not a tuple of (int, func);"); z_proxy_return(self, FALSE); } if (type != PROXY_SSL_HS_POLICY) { z_policy_var_unref(args); z_proxy_log(self, CORE_POLICY, 1, "Invalid handshake hash item, only PROXY_SSL_HS_POLICY is supported; type='%d'", type); z_proxy_return(self, FALSE); } /* NOTE: z_policy_call_object consumes args */ res = z_policy_call_object(cb, args, self->session_id); if (res) { if (!z_policy_var_parse(res, "i", retval)) z_proxy_log(self, CORE_POLICY, 1, "Handshake callback returned non-int;"); else rc = TRUE; } z_policy_var_unref(res); z_proxy_return(self, rc); } static gboolean z_proxy_ssl_load_local_key(ZProxySSLHandshake *handshake) { ZProxy *self = handshake->proxy; guint ndx = handshake->side; ZSSLSession *session = handshake->session; SSL *ssl; guint policy_type; z_proxy_enter(self); ssl = session->ssl; z_policy_lock(self->thread); if (!z_proxy_ssl_callback(self, ndx, "setup_key", z_policy_var_build("(i)", ndx), &policy_type) || policy_type != PROXY_SSL_HS_ACCEPT) { z_policy_unlock(self->thread); z_proxy_log(self, CORE_POLICY, 1, "Error fetching local key/certificate pair; side='%s'", EP_STR(ndx)); z_proxy_return(self, FALSE); } z_policy_unlock(self->thread); if (self->ssl_opts.local_privkey[ndx] && self->ssl_opts.local_cert[ndx]) { SSL_use_PrivateKey(ssl, self->ssl_opts.local_privkey[ndx]); SSL_use_certificate(ssl, self->ssl_opts.local_cert[ndx]); } else if (ndx == EP_CLIENT) { z_proxy_log(self, CORE_ERROR, 3, "No local key is set for the client side, either missing keys " "or misconfigured keybridge, the SSL handshake will probably fail."); } z_proxy_return(self, TRUE); } static gboolean z_proxy_ssl_load_local_ca_list(ZProxySSLHandshake *handshake) { ZProxy *self = handshake->proxy; guint ndx = handshake->side; ZSSLSession *session = handshake->session; int i, n; X509_STORE *ctx; guint policy_type; z_proxy_enter(self); z_policy_lock(self->thread); if (!z_proxy_ssl_callback(self, ndx, "setup_ca_list", z_policy_var_build("(i)", ndx), &policy_type) || policy_type != PROXY_SSL_HS_ACCEPT) { z_policy_unlock(self->thread); z_proxy_log(self, CORE_POLICY, 1, "Error fetching local trusted CA list; side='%s'", EP_STR(ndx)); z_proxy_return(self, FALSE); } z_policy_unlock(self->thread); if (ndx == EP_CLIENT) { STACK_OF(X509_NAME) *sk; sk = sk_X509_NAME_new_null(); if (!sk) z_proxy_return(self, FALSE); n = sk_X509_num(self->ssl_opts.local_ca_list[ndx]); for (i = 0; i < n; i++) sk_X509_NAME_push(sk, X509_NAME_dup(X509_get_subject_name(sk_X509_value(self->ssl_opts.local_ca_list[ndx], i)))); SSL_set_client_CA_list(session->ssl, sk); } ctx = session->ssl->ctx->cert_store; n = sk_X509_num(self->ssl_opts.local_ca_list[ndx]); for (i = 0; i < n; i++) X509_STORE_add_cert(ctx, sk_X509_value(self->ssl_opts.local_ca_list[ndx], i)); z_proxy_return(self, TRUE); } static gboolean z_proxy_ssl_load_local_crl_list(ZProxySSLHandshake *handshake, gchar *name) { guint ndx = handshake->side; ZSSLSession *session = handshake->session; ZProxy *self = handshake->proxy; X509_STORE *ctx = session->ssl->ctx->cert_store; guint policy_type; int i; z_proxy_enter(self); z_policy_lock(self->thread); if (!z_proxy_ssl_callback(self, ndx, "setup_crl_list", z_policy_var_build("(si)", name, ndx), &policy_type) || policy_type != PROXY_SSL_HS_ACCEPT) { z_policy_unlock(self->thread); z_proxy_log(self, CORE_POLICY, 1, "Error fetching CRL list for CA; side='%s', ca='%s'", EP_STR(ndx), name); z_proxy_return(self, FALSE); } z_policy_unlock(self->thread); for (i = 0; i < sk_X509_CRL_num(self->ssl_opts.local_crl_list[ndx]); i++) { X509_CRL *crl; char buf[512]; crl = sk_X509_CRL_value(self->ssl_opts.local_crl_list[ndx], i); X509_NAME_oneline(X509_CRL_get_issuer(crl), buf, sizeof(buf)); if (strcmp(buf, name) == 0) X509_STORE_add_crl(ctx, crl); } z_proxy_return(self, TRUE); } /* this function is called to verify the whole chain as provided by the peer. The SSL lib takes care about setting up the context, we only need to call X509_verify_cert. */ static int z_proxy_ssl_app_verify_cb(X509_STORE_CTX *ctx, void *user_data) { ZProxySSLHandshake *handshake = (ZProxySSLHandshake *) user_data; ZProxy *self = handshake->proxy; gint side = handshake->side; gboolean new_verify_callback, success; guint verdict; gboolean ok, verify_valid; gint verify_error, verify_type; z_proxy_enter(self); /* publish the peer's certificate to python, and fetch the calist required to verify the certificate */ if (self->ssl_opts.peer_cert[side]) X509_free(self->ssl_opts.peer_cert[side]); self->ssl_opts.peer_cert[side] = ctx->cert; CRYPTO_add(&ctx->cert->references, 1, CRYPTO_LOCK_X509); verify_type = self->ssl_opts.verify_type[side]; new_verify_callback = z_proxy_ssl_callback_exists(self, side, "verify_cert_ext"); if (side == EP_SERVER) z_proxy_ssl_load_local_ca_list(handshake); verify_valid = X509_verify_cert(ctx); verify_error = X509_STORE_CTX_get_error(ctx); if (self->ssl_opts.permit_missing_crl && !verify_valid && verify_error == X509_V_ERR_UNABLE_TO_GET_CRL) { /* no CRL was found, but the configuration explicitly permits * missing CRLs */ /*LOG This message indicates that the CRL of a CA in the certificate chain was not found during verification, but the certificate is still being accepted due to the proxy configuration explicitly allowing missing CRLs. */ z_proxy_log(self, CORE_POLICY, 5, "Accepting certficate even though CRL was missing as directed by the policy"); verify_valid = TRUE; verify_error = X509_V_OK; } z_policy_lock(self->thread); if (new_verify_callback) success = z_proxy_ssl_callback(self, side, "verify_cert_ext", z_policy_var_build("(i(ii))", side, verify_valid, verify_error), &verdict); else success = z_proxy_ssl_callback(self, side, "verify_cert", z_policy_var_build("(i)", side), &verdict); z_policy_unlock(self->thread); if (!success) { ok = 0; goto exit; } if (verdict == PROXY_SSL_HS_ACCEPT) { if (verify_type == PROXY_SSL_VERIFY_REQUIRED_TRUSTED || verify_type == PROXY_SSL_VERIFY_OPTIONAL_TRUSTED) { ok = verify_valid; } else if (verify_type == PROXY_SSL_VERIFY_REQUIRED_UNTRUSTED || verify_type == PROXY_SSL_VERIFY_OPTIONAL_UNTRUSTED) { if (!verify_valid && (self->ssl_opts.permit_invalid_certificates || (verify_error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || verify_error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN || verify_error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || verify_error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT || verify_error == X509_V_ERR_CERT_UNTRUSTED || verify_error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))) { z_proxy_log(self, CORE_POLICY, 3, "Accepting untrusted certificate as directed by the policy; verify_error='%s'", X509_verify_cert_error_string(verify_error)); ok = 1; } else { ok = verify_valid; } } else { ok = 1; } } else if (verdict == PROXY_SSL_HS_VERIFIED) { if (!verify_valid) z_proxy_log(self, CORE_POLICY, 3, "Accepting untrusted certificate as directed by the policy; verify_error='%s'", X509_verify_cert_error_string(verify_error)); ok = 1; } else { ok = 0; } exit: z_proxy_return(self, ok); } /* verify callback of the X509_STORE we set up when verifying the peer's certificate. We are checking the CRLs here */ static int z_proxy_ssl_verify_peer_cert_cb(int ok, X509_STORE_CTX *ctx) { SSL *ssl = (SSL *) X509_STORE_CTX_get_app_data(ctx); ZProxySSLHandshake *handshake = (ZProxySSLHandshake *) SSL_get_app_data(ssl); ZProxy *self = handshake->proxy; gint side = handshake->side; X509_OBJECT obj; X509_CRL *crl; X509_NAME *subject, *issuer; int rc; char subject_name[512], issuer_name[512]; int depth, verify_error; z_proxy_enter(self); /* if self->current_cert is a CA certificate, it should have a CRL list referenced by its "subject". We check the validity of the CRL when the CA certificate is verified. if self->current_cert is either a subordinate CA or a simple X509 certificate, we must check whether the issuer's CRL has revoked our cert. As openssl calls us in the chain order, verification of the CRL had already been performed at this time. */ depth = X509_STORE_CTX_get_error_depth(ctx); verify_error = X509_STORE_CTX_get_error(ctx); subject = X509_get_subject_name(ctx->current_cert); X509_NAME_oneline(subject, subject_name, sizeof(subject_name)); issuer = X509_get_issuer_name(ctx->current_cert); X509_NAME_oneline(issuer, issuer_name, sizeof(issuer_name)); if (!ok && !(self->ssl_opts.permit_missing_crl && verify_error == X509_V_ERR_UNABLE_TO_GET_CRL)) { /* Do not log an error if the issue was a missing CRL and the policy explicitly * permits missing CRLs. */ z_proxy_log(self, CORE_POLICY, 1, "Certificate verification failed; error='%s'", X509_verify_cert_error_string(verify_error)); } z_proxy_log(self, CORE_DEBUG, 6, "Verifying certificate; issuer='%s', subject='%s'", issuer_name, subject_name); if (self->ssl_opts.verify_depth[side] < depth) { ok = 0; z_proxy_log(self, CORE_POLICY, 1, "Certificate verification failed; error='%s', " "side='%s', max_depth='%d', depth='%d'", X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG), EP_STR(side), self->ssl_opts.verify_depth[side], depth); } z_proxy_ssl_load_local_crl_list(handshake, subject_name); rc = X509_STORE_get_by_subject(ctx, X509_LU_CRL, subject, &obj); if (rc == 1 && obj.type == X509_LU_CRL) { EVP_PKEY *pkey; /* we are checking a CA certificate, and it has an associated CRL */ crl = obj.data.crl; z_proxy_log(self, CORE_DEBUG, 6, "Verifying CRL integrity; issuer='%s'", subject_name); pkey = X509_get_pubkey(ctx->current_cert); if (X509_CRL_verify(crl, pkey) <= 0) { EVP_PKEY_free(pkey); X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); z_proxy_log(self, CORE_ERROR, 1, "Invalid signature on CRL; issuer='%s'", issuer_name); goto error_free; } EVP_PKEY_free(pkey); rc = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); if (rc == 0) { /*LOG This message indicates an invalid Certificate Revocation List (CRL), because it has an invalid nextUpdate field. */ z_proxy_log(self, CORE_ERROR, 1, "CRL has invalid nextUpdate field; issuer='%s'", subject_name); X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); goto error_free; } if (rc <= 0) { /*LOG This message indicates an invalid Certificate Revocation List (CRL), because it is expired. */ z_proxy_log(self, CORE_ERROR, 1, "CRL is expired; issuer='%s'", subject_name); X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED); goto error_free; } X509_OBJECT_free_contents(&obj); } else if (depth > 0 && !self->ssl_opts.permit_missing_crl) { /*LOG This message indicates that no certificate revocation list was found for a CA certificate, and Zorp configuration requires that a CRL must be present. */ z_proxy_log(self, CORE_ERROR, 1, "CRL not found for certificate; subject='%s'", subject_name); ok = 0; } /* verify whether the issuer has revoked this certificate */ rc = X509_STORE_get_by_subject(ctx, X509_LU_CRL, issuer, &obj); if (rc == 1 && obj.type == X509_LU_CRL) { STACK_OF(X509_REVOKED) *revoked_list; X509_REVOKED *revoked; ASN1_INTEGER *cert_serial; int n, i; cert_serial = X509_get_serialNumber(ctx->current_cert); z_proxy_log(self, CORE_DEBUG, 6, "Verifying certificate against CRL; cert='%s', serial='%ld', issuer='%s'", subject_name, ASN1_INTEGER_get(cert_serial), issuer_name); crl = obj.data.crl; revoked_list = X509_CRL_get_REVOKED(crl); n = sk_X509_REVOKED_num(revoked_list); for (i = 0; i < n; i++) { revoked = sk_X509_REVOKED_value(revoked_list, i); if (ASN1_INTEGER_cmp(revoked->serialNumber, cert_serial) == 0) { BIO *bio; char serial_str[128]; char *ptr; X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); bio = BIO_new(BIO_s_mem()); if (bio) { unsigned long len; i2a_ASN1_INTEGER(bio, revoked->serialNumber); len = BIO_get_mem_data(bio, &ptr); len = MIN(len, sizeof(serial_str) - 1); memcpy(serial_str, ptr, len); serial_str[len] = 0; z_proxy_log(self, CORE_ERROR, 1, "Certificate revoked by CRL; issuer='%s', serial='%s'", issuer_name, serial_str); BIO_free_all(bio); } goto error_free; } } X509_OBJECT_free_contents(&obj); } else if (!self->ssl_opts.permit_missing_crl) { /*LOG This message indicates that no certificate revocation list was found for a CA certificate, and Zorp configuration requires that a CRL must be present. */ z_proxy_log(self, CORE_ERROR, 1, "CRL not found for certificate; issuer='%s'", issuer_name); ok = 0; } z_proxy_return(self, ok); error_free: X509_OBJECT_free_contents(&obj); z_proxy_return(self, 0); } static int z_proxy_ssl_client_cert_cb(SSL *ssl, X509 **cert, EVP_PKEY **pkey) { ZProxySSLHandshake *handshake = (ZProxySSLHandshake *) SSL_get_app_data(ssl); ZProxy *self = handshake->proxy;; gint side = handshake->side; gint res; z_proxy_enter(self); /* publish peer's idea of its trusted certificate authorities */ if (ssl->s3 && ssl->s3->tmp.ca_names) { int i, n; n = sk_X509_NAME_num(ssl->s3->tmp.ca_names); for (i = 0; i < n; i++) { X509_NAME *v; v = sk_X509_NAME_value(ssl->s3->tmp.ca_names, i); sk_X509_NAME_push(self->ssl_opts.server_peer_ca_list, X509_NAME_dup(v)); } } if (!z_proxy_ssl_load_local_key(handshake)) z_proxy_return(self, 0); if (self->ssl_opts.local_cert[side] && self->ssl_opts.local_privkey[side]) { *cert = self->ssl_opts.local_cert[side]; *pkey = self->ssl_opts.local_privkey[side]; CRYPTO_add(&(*cert)->references, 1, CRYPTO_LOCK_X509); CRYPTO_add(&(*pkey)->references, 1, CRYPTO_LOCK_EVP_PKEY); res = 1; } else { *cert = NULL; *pkey = NULL; res = 0; } z_proxy_return(self, res); } static gboolean z_proxy_ssl_handshake_timeout(gpointer user_data) { ZProxySSLHandshake *handshake = (ZProxySSLHandshake *) user_data; z_proxy_enter(handshake->proxy); z_proxy_log(handshake->proxy, CORE_ERROR, 1, "SSL handshake timed out; side='%s'", EP_STR(handshake->side)); z_proxy_ssl_handshake_set_error(handshake, SSL_ERROR_ZERO_RETURN); /* call completion callback */ z_proxy_leave(handshake->proxy); z_proxy_ssl_handshake_call_callback(handshake); return FALSE; } /** * Callback function set up as read and write callback on the stream we are doing * the SSL handshake on. * * @param stream the stream instance * @param poll_cond the condition that caused this callback to be called * @param s user data used by the callback (the handshake object in our case) * * This function is used to repeatedly call either SSL_accept() or SSL_connect() until * OpenSSL reports that the handshake is either finished or failed. * * The function sets the G_IO_IN / G_IO_OUT conditions of the underlying stream to * comply with the requests of OpenSSL. * * Upon termination of the handshake the callback function set in the handshake object * is called with the handshake structure and the user data pointer passed as arguments. * This callback function can be used to signal the called that the handshake has been * finished. * * Upon exiting, the 'ssl_err' member of the handshake object is set to zero on successful * handshake, otherwise it contains the OpenSSL error code, and the string representation * of the error is in 'ssl_err_str'. Use z_proxy_ssl_handshake_get_error() and * z_proxy_ssl_handshake_get_error_str() to query the error code / description. * * @return TRUE if needs to be called again */ static gboolean z_proxy_ssl_handshake_cb(ZStream *stream, GIOCondition poll_cond G_GNUC_UNUSED, gpointer s) { ZProxySSLHandshake *handshake = (ZProxySSLHandshake *) s; X509 *peercert = NULL; gint result; z_proxy_enter(handshake->proxy); if (handshake->side == EP_CLIENT) result = SSL_accept(handshake->session->ssl); else result = SSL_connect(handshake->session->ssl); if (result <= 0) { gint ssl_err = SSL_get_error(handshake->session->ssl, result); switch (ssl_err) { case SSL_ERROR_WANT_READ: z_stream_set_cond(stream, G_IO_IN, TRUE); z_stream_set_cond(stream, G_IO_OUT, FALSE); break; case SSL_ERROR_WANT_WRITE: z_stream_set_cond(stream, G_IO_IN, FALSE); z_stream_set_cond(stream, G_IO_OUT, TRUE); break; case SSL_ERROR_SYSCALL: if (z_errno_is(EAGAIN) || z_errno_is(EINTR)) break; if (z_errno_is(0)) { z_proxy_ssl_handshake_set_error(handshake, ssl_err); z_proxy_log(handshake->proxy, CORE_ERROR, 1, "SSL handshake failed, EOF received; side='%s'", EP_STR(handshake->side)); goto done; } /* no break here: we let the code go to the next case so that the error gets logged */ default: /* SSL handshake failed */ z_proxy_ssl_handshake_set_error(handshake, ssl_err); z_proxy_log(handshake->proxy, CORE_ERROR, 1, "SSL handshake failed; side='%s', error='%s'", EP_STR(handshake->side), z_proxy_ssl_handshake_get_error_str(handshake)); goto done; } z_proxy_return(handshake->proxy, TRUE); } /* handshake completed */ z_proxy_ssl_handshake_set_error(handshake, 0); /* print peer certificate info */ peercert = SSL_get_peer_certificate(handshake->session->ssl); if (peercert && z_log_enabled(CORE_DEBUG, 4)) { gchar name[1024]; gchar issuer[1024]; BIO *bio; char serial_str[128]; char *ptr; long version = X509_get_version(peercert); bio = BIO_new(BIO_s_mem()); if (bio) { unsigned long len; i2a_ASN1_INTEGER(bio, X509_get_serialNumber(peercert)); len = BIO_get_mem_data(bio, &ptr); len = MIN(len, sizeof(serial_str) - 1); memcpy(serial_str, ptr, len); serial_str[len] = 0; X509_NAME_oneline(X509_get_subject_name(peercert), name, sizeof(name) - 1); X509_NAME_oneline(X509_get_issuer_name(peercert), issuer, sizeof(issuer) - 1); z_proxy_log(handshake->proxy, CORE_DEBUG, 4, "Identified peer; side='%s', peer='%s', " "issuer='%s', serial='%s', version='%lu'", EP_STR(handshake->side), name, issuer, serial_str, version); BIO_free_all(bio); } } if (peercert) X509_free(peercert); done: z_proxy_leave(handshake->proxy); z_proxy_ssl_handshake_call_callback(handshake); return TRUE; } /** * Save stream state and set up our callbacks driving the SSL handshake. * * @param handshake the handshake object * @param proxy_group the proxy group whose poll will drive the handshake * * This function saves the stream state into the handshake object, and then sets up * the stream callbacks and conditions to our callbacks that will call SSL_accept() / * SSL_connect() while the operation has been completed. * * Depending on which side we're setting up the handshake, either G_IO_IN or G_IO_OUT is * set initially. * * @return TRUE if setting up the stream was successful */ static gboolean z_proxy_ssl_setup_stream(ZProxySSLHandshake *handshake, ZProxyGroup *proxy_group) { z_proxy_enter(handshake->proxy); /* save stream callback state */ if (!z_stream_save_context(handshake->stream, &handshake->stream_context)) { z_proxy_log(handshake->proxy, CORE_ERROR, 3, "Failed to save stream context;"); z_proxy_return(handshake->proxy, FALSE); } /* set up our own callbacks doing the handshake */ z_stream_set_callback(handshake->stream, G_IO_IN, z_proxy_ssl_handshake_cb, handshake, NULL); z_stream_set_callback(handshake->stream, G_IO_OUT, z_proxy_ssl_handshake_cb, handshake, NULL); z_stream_set_nonblock(handshake->stream, TRUE); /* set up our timeout source */ handshake->timeout = z_timeout_source_new(handshake->proxy->ssl_opts.handshake_timeout); g_source_set_callback(handshake->timeout, z_proxy_ssl_handshake_timeout, handshake, NULL); g_source_attach(handshake->timeout, z_proxy_group_get_context(proxy_group)); /* attach stream to the poll of the proxy group */ z_stream_attach_source(handshake->stream, z_proxy_group_get_context(proxy_group)); z_stream_set_cond(handshake->stream, G_IO_PRI, FALSE); z_stream_set_cond(handshake->stream, G_IO_IN, (handshake->side == EP_CLIENT)); z_stream_set_cond(handshake->stream, G_IO_OUT, (handshake->side == EP_SERVER)); z_proxy_return(handshake->proxy, TRUE); } /** * Restore stream state to the pre-handshake values. * * @param handshake the handshake object * * This function re-sets the stream state to the pre-handshake state saved by * z_proxy_ssl_setup_stream(). * * @return TRUE if restoring up the stream was successful */ static gboolean z_proxy_ssl_restore_stream(ZProxySSLHandshake *handshake) { gboolean res = TRUE; z_proxy_enter(handshake->proxy); if (handshake->timeout) { g_source_destroy(handshake->timeout); g_source_unref(handshake->timeout); handshake->timeout = NULL; } z_stream_detach_source(handshake->stream); if (!z_stream_restore_context(handshake->stream, &handshake->stream_context)) { z_proxy_log(handshake->proxy, CORE_ERROR, 3, "Failed to restore stream context;"); res = FALSE; } z_proxy_return(handshake->proxy, res); } /** * Completion callback used for our semi-nonblocking handshake. * * @param handshake the handshake object * @param user_data the gboolean which has to be set * * This function is used as a completion callback by z_proxy_ssl_do_handshake() if it's * doing a semi-nonblocking handshake, where it avoids starvation of other proxies running * in the same proxy group by iterating the main loop of the proxy group and waiting * for the handshake to be finished. * * The callback is passed a pointer to a gboolean: z_proxy_ssl_do_handshake() iterates * the main loop until the boolean is set by the callback, signaling that the handshake * has been finished. */ static void z_proxy_ssl_handshake_completed(ZProxySSLHandshake *handshake G_GNUC_UNUSED, gpointer user_data) { z_enter(); *((gboolean *) user_data) = TRUE; z_leave(); } /** * Do an SSL handshake with blocking semantics. * * @param handshake the handshake object * @param nonblocking whether or not to do a semi-nonblocking handshake * * This function initiates an SSL handshake and waits for it to be finished. The handshake * is either done in a true blocking manner, where the underlying stream is blocking, or * in a semi-nonblocking one, where the underlying stream is nonblocking but we iterate * the proxy group main loop until the handshake is finished. * * @return TRUE if the handshake was successful, FALSE otherwise */ static gboolean z_proxy_ssl_do_handshake(ZProxySSLHandshake *handshake, gboolean nonblocking) { z_proxy_enter(handshake->proxy); if (nonblocking) { ZProxyGroup *proxy_group = z_proxy_get_group(handshake->proxy); gboolean handshake_done = FALSE; z_proxy_ssl_handshake_set_callback(handshake, z_proxy_ssl_handshake_completed, &handshake_done, NULL); if (!z_proxy_ssl_setup_stream(handshake, proxy_group)) z_proxy_return(handshake->proxy, FALSE); /* iterate until the handshake has been completed */ while (!handshake_done && z_proxy_group_iteration(proxy_group)) { ; } if (!z_proxy_ssl_restore_stream(handshake)) z_proxy_return(handshake->proxy, FALSE); } else { /* non-blocking handshake, call the callback directly: the underlying * stream (and thus the BIO) is in blocking mode, so SSL_accept()/SSL_connect() * is done */ z_stream_set_timeout(handshake->stream, handshake->proxy->ssl_opts.handshake_timeout); z_proxy_ssl_handshake_cb(handshake->stream, 0, (gpointer) handshake); z_stream_set_timeout(handshake->stream, -2); } z_proxy_return(handshake->proxy, (z_proxy_ssl_handshake_get_error(handshake) == 0)); } /** * Setup the various parameters (certs, keys, etc.) and callbacks used by the SSL handshake. * * @param handshake the handshake object * * This function initiates the SSL session that is used by the handshake. It sets up basic * handshake parameters (like the SSL methods we support, cipher specs, etc.) and the * callback functions that will be used by OpenSSL to verify certificates. * * @return TRUE if setting up the parameters/callbacks has succeeded, FALSE otherwise */ static gboolean z_proxy_ssl_setup_handshake(ZProxySSLHandshake *handshake) { ZProxy *self = handshake->proxy; gint side = handshake->side; SSL_CTX *ctx; SSL *tmpssl; ZSSLSession *ssl; int verify_mode = 0; gsize buffered_bytes; z_proxy_enter(self); z_proxy_log(self, CORE_DEBUG, 6, "Performing SSL handshake; side='%s'", EP_STR(side)); /* check for cases where plain text injection is possible: before * starting the SSL handshake all stream buffers above the SSL * stream *must* be empty, otherwise it would be possible for the * proxy to read bytes sent *before* the SSL handshake in a context * where it thinks that all following communication is * SSL-protected */ if ((buffered_bytes = z_stream_get_buffered_bytes(handshake->stream)) > 0) { z_proxy_log(self, CORE_ERROR, 1, "Protocol error: possible clear text injection, " "buffers above the SSL stream are not empty; bytes='%zu'", buffered_bytes); z_proxy_return(self, FALSE); } if (strcmp(self->ssl_opts.ssl_method[side]->str, "SSLv23") == 0) { if (side == EP_CLIENT) ctx = SSL_CTX_new(SSLv23_server_method()); else ctx = SSL_CTX_new(SSLv23_client_method()); } #ifndef OPENSSL_NO_SSL2 else if (strcmp(self->ssl_opts.ssl_method[side]->str, "SSLv2") == 0) { if (side == EP_CLIENT) ctx = SSL_CTX_new(SSLv2_server_method()); else ctx = SSL_CTX_new(SSLv2_client_method()); } #endif else if (strcmp(self->ssl_opts.ssl_method[side]->str, "SSLv3") == 0) { if (side == EP_CLIENT) ctx = SSL_CTX_new(SSLv3_server_method()); else ctx = SSL_CTX_new(SSLv3_client_method()); } else if (strcmp(self->ssl_opts.ssl_method[side]->str, "TLSv1") == 0) { if (side == EP_CLIENT) ctx = SSL_CTX_new(TLSv1_server_method()); else ctx = SSL_CTX_new(TLSv1_client_method()); } else { z_proxy_log(self, CORE_POLICY, 1, "Bad SSL method; method='%s', side='%s'", self->ssl_opts.ssl_method[side]->str, EP_STR(side)); z_proxy_return(self, FALSE); } if (!ctx) { z_proxy_log(self, CORE_ERROR, 1, "Error allocating SSL_CTX struct;"); z_proxy_return(self, FALSE); } if (!SSL_CTX_set_cipher_list(ctx, self->ssl_opts.ssl_cipher[side]->str)) { z_proxy_log(self, CORE_ERROR, 1, "Error setting cipher spec; ciphers='%s', side='%s'", self->ssl_opts.ssl_cipher[side]->str, EP_STR(side)); z_proxy_return(self, FALSE); } SSL_CTX_set_options(ctx, SSL_OP_ALL | (self->ssl_opts.disable_proto_sslv2[side] ? SSL_OP_NO_SSLv2 : 0) | (self->ssl_opts.disable_proto_sslv3[side] ? SSL_OP_NO_SSLv3 : 0) | (self->ssl_opts.disable_proto_tlsv1[side] ? SSL_OP_NO_TLSv1 : 0)); if (side == EP_SERVER) SSL_CTX_set_client_cert_cb(ctx, z_proxy_ssl_client_cert_cb); /* instead of specifying key here */ /* For server side, the z_proxy_ssl_app_verify_callback_cb sets up trusted CA list. It calls verify_cert callback for both sides. Releasing the handshake reference is done by the callback. */ SSL_CTX_set_cert_verify_callback(ctx, z_proxy_ssl_app_verify_cb, handshake); if (self->ssl_opts.verify_type[side] == PROXY_SSL_VERIFY_REQUIRED_TRUSTED) verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; else if (self->ssl_opts.verify_type[side] == PROXY_SSL_VERIFY_REQUIRED_UNTRUSTED || self->ssl_opts.verify_type[side] == PROXY_SSL_VERIFY_OPTIONAL_UNTRUSTED || self->ssl_opts.verify_type[side] == PROXY_SSL_VERIFY_OPTIONAL_TRUSTED) verify_mode = SSL_VERIFY_PEER; if (verify_mode) SSL_CTX_set_verify(ctx, verify_mode, z_proxy_ssl_verify_peer_cert_cb); if (self->ssl_opts.verify_ca_directory[side] != NULL || self->ssl_opts.verify_crl_directory[side] != NULL) { X509_LOOKUP *lookup = X509_STORE_add_lookup(ctx->cert_store, X509_LOOKUP_hash_dir()); if (self->ssl_opts.verify_ca_directory[side] != NULL) X509_LOOKUP_add_dir(lookup, self->ssl_opts.verify_ca_directory[side]->str, X509_FILETYPE_PEM); if (self->ssl_opts.verify_crl_directory[side] != NULL) { X509_LOOKUP_add_dir(lookup, self->ssl_opts.verify_crl_directory[side]->str, X509_FILETYPE_PEM); X509_STORE_set_flags(ctx->cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } } tmpssl = SSL_new(ctx); SSL_set_options(tmpssl, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_set_app_data(tmpssl, handshake); /* Give the SSL context to the handshake class after cleaning up the current one */ if (handshake->ssl_context) SSL_CTX_free(handshake->ssl_context); handshake->ssl_context = ctx; if (!tmpssl) { z_proxy_log(self, CORE_ERROR, 1, "Error allocating SSL struct; side='%s'", EP_STR(side)); z_proxy_return(self, FALSE); } if (handshake->session) z_ssl_session_unref(handshake->session); ssl = handshake->session = z_ssl_session_new_ssl(tmpssl); SSL_free(tmpssl); if (!ssl) { z_proxy_log(self, CORE_ERROR, 1, "Error creating SSL session; side='%s'", EP_STR(side)); z_proxy_return(self, FALSE); } if (side == EP_CLIENT) { if (!z_proxy_ssl_load_local_key(handshake) || !z_proxy_ssl_load_local_ca_list(handshake)) z_proxy_return(self, FALSE); } z_stream_ssl_set_session(handshake->stream, ssl); z_proxy_return(self, TRUE); } /** * Perform an SSL handshake with blocking semantics. * * @param handshake the handshake object * * This function sets up the handshake parameters and then does the SSL handshake. If * the proxy associated with the handshake has the ZPF_NONBLOCKING flag set, it does a * semi-nonblocking handshake to avoid starvation of other proxies running in the same * proxy group. * * @return TRUE if the handshake was successful, FALSE otherwise */ gboolean z_proxy_ssl_perform_handshake(ZProxySSLHandshake *handshake) { ZProxy *self = handshake->proxy; gboolean res; gsize buffered_bytes; z_proxy_enter(self); if (!z_proxy_ssl_setup_handshake(handshake)) z_proxy_return(self, FALSE); res = z_proxy_ssl_do_handshake(handshake, self->flags & ZPF_NONBLOCKING); /* SSL plain injection check: although we do check that the stream * buffers above the SSL stream are empty, but if there's a bug * somewhere in the SSL handshake code/polling/etc. it still might * be possible that we have buffered data above the SSL layer. */ if ((buffered_bytes = z_stream_get_buffered_bytes(handshake->stream)) > 0) { z_proxy_log(self, CORE_ERROR, 1, "Internal error, buffers above the SSL " "stream are not empty after handshake; bytes='%zu'", buffered_bytes); z_proxy_return(self, FALSE); } z_proxy_return(self, res); } /** * Do initial SSL setup of a proxy endpoint stream. * * @param self the proxy instance * @param side the side being set up * * Based on the policy security settings, this function pushed an SSL stream onto the * stream stack used on the specified endpoint of the proxy and requests a handshake * to be initiated. * * The SSL stream is pushed onto the stack if the security level is greater that 'NONE', * that is, there's any possibility that we'll have to use SSL on the endpoint. (The SSL * stream instance has its session set to NULL, that is, it's not actually doing * encapsulation initially.) * * The handshake is initiated only if the endpoint is in 'FORCE_SSL' mode, that is, an * SSL handshake precedes all protocol communication on the stream. * * @return TRUE if setup was successful, FALSE otherwise */ gboolean z_proxy_ssl_init_stream(ZProxy *self, gint side) { gboolean rc = TRUE; z_proxy_enter(self); if (self->ssl_opts.security[side] > PROXY_SSL_SEC_NONE) { ZStream *old; old = self->endpoints[side]; self->endpoints[side] = z_stream_ssl_new(old, NULL); z_stream_unref(old); /* do an SSL handshake right away if we're in forced SSL mode */ if (self->ssl_opts.security[side] == PROXY_SSL_SEC_FORCE_SSL) rc = z_proxy_ssl_request_handshake(self, side, FALSE); } z_proxy_return(self, rc); } /** * Start an asynchronous SSL handshake. * * @param handshake the handshake object * @param cb callback function to be called on completion * @param user_data user_data passed to the callback * * This function sets up handshake parameters, sets up stream callbacks / conditions * and adds the stream to the context of the proxy group. * * The callback is called when the handshake has been completed: either by finishing a * successful SSL handshake or by failing the handshake. * * @return TRUE if starting up the handshake was successful, FALSE otherwise */ static gboolean z_proxy_ssl_perform_handshake_async(ZProxySSLHandshake *handshake, ZProxySSLCallbackFunc cb, gpointer user_data, GDestroyNotify user_data_notify) { ZProxyGroup *proxy_group = z_proxy_get_group(handshake->proxy); z_proxy_enter(handshake->proxy); if (!z_proxy_ssl_setup_handshake(handshake)) z_proxy_return(handshake->proxy, FALSE); z_proxy_ssl_handshake_set_callback(handshake, cb, user_data, user_data_notify); if (!z_proxy_ssl_setup_stream(handshake, proxy_group)) z_proxy_return(handshake->proxy, FALSE); z_proxy_return(handshake->proxy, TRUE); } /** * Completion callback function used by the client-side non-blocking handshake * * @param handshake the handshake object * @param user_data user_data passed to the callback * * This function is called when the client-side SSL handshake has been completed for * a non-blocking proxy instance. * * The function restores the stream state to the pre-handshake state, stores the SSL session, * frees the handshake object and then calls z_proxy_nonblocking_init() for the proxy * instance. */ static void z_proxy_ssl_init_completed(ZProxySSLHandshake *handshake, gpointer user_data) { ZProxy *self = handshake->proxy; gboolean success = FALSE; z_enter(); g_assert(handshake == user_data); /* restore stream state to that of before the handshake */ if (!z_proxy_ssl_restore_stream(handshake)) z_proxy_return(self); success = (z_proxy_ssl_handshake_get_error(handshake) == 0); /* if the handshake was successful, set the session and call nonblocking init */ if (success) { if (self->ssl_opts.ssl_sessions[handshake->side]) z_proxy_ssl_clear_session(self, handshake->side); self->ssl_opts.ssl_sessions[handshake->side] = z_ssl_session_ref(handshake->session); /* call the nonblocking init callback of the proxy */ success = z_proxy_nonblocking_init(self, z_proxy_group_get_poll(z_proxy_get_group(self))); } if (!success) { /* initializing the client stream or the proxy failed, stop the proxy instance */ z_proxy_nonblocking_stop(self); } z_leave(); } /** * Initiate SSL handshake for a non-blocking proxy. * * @param self the proxy instance * @param side the side being initialized * * This function is called from the proxy core when it's starting up a new non-blocking * proxy instance. * * If the configured handshake order is (client, server) then we can do a true non-blocking * handshake where the nonblocking init callback of the proxy is called as a continuation * after the handshake. * * In all other cases the function falls back to doing a semi-nonblocking handshake by * calling z_proxy_ssl_init_stream(). * * @return TRUE if the setup (and possible handshake) succeeded, FALSE otherwise */ gboolean z_proxy_ssl_init_stream_nonblocking(ZProxy *self, gint side) { gboolean res = TRUE; z_proxy_enter(self); if (self->ssl_opts.security[side] > PROXY_SSL_SEC_NONE) { /* we support async handshake only on the client side, and only if handshake order * is (client, server) */ if ((side == EP_CLIENT) && self->ssl_opts.handshake_seq == PROXY_SSL_HS_CLIENT_SERVER) { ZProxySSLHandshake *handshake; ZStream *old; old = self->endpoints[side]; self->endpoints[side] = z_stream_ssl_new(old, NULL); z_stream_unref(old); handshake = z_proxy_ssl_handshake_new(self, self->endpoints[side], side); res = z_proxy_ssl_perform_handshake_async(handshake, z_proxy_ssl_init_completed, handshake, NULL); } else { res = z_proxy_ssl_init_stream(self, side); if (res) res = z_proxy_nonblocking_init(self, z_proxy_group_get_poll(z_proxy_get_group(self))); } } else res = z_proxy_nonblocking_init(self, z_proxy_group_get_poll(z_proxy_get_group(self))); z_proxy_return(self, res); } /** * Request an SSL handshake to be done on one of the proxy endpoints. * * @param self the proxy instance * @param side the side the handshake is to be made on * @param forced is this a forced handshake * * This function initiates an SSL handshake on one of both of the proxy * endpoints, depending on the SSL settings configured in the policy. * * If forced is TRUE, the function always does an SSL handshake on the * requested side independently of the handshake order configured. * * @return TRUE if the handshake was successful, FALSE if not */ gboolean z_proxy_ssl_request_handshake(ZProxy *self, gint side, gboolean forced) { gboolean rc = FALSE; ZProxySSLHandshake *handshake; z_proxy_enter(self); /* if already initialized, return right away */ if (self->ssl_opts.ssl_sessions[side]) z_proxy_return(self, TRUE); /* if the proxy requested that we force-connect to the server and * we're doing handshake at the client side, we have to connect * first */ if ((side == EP_CLIENT) && self->ssl_opts.force_connect_at_handshake) { z_proxy_log(self, CORE_INFO, 6, "Force-establishing server connection since the configured handshake order requires it;"); if (!z_proxy_connect_server(self, NULL, 0)) { z_proxy_log(self, CORE_ERROR, 3, "Server connection failed to establish, giving up;"); z_proxy_return(self, FALSE); } } /* we don't delay the handshake if: * - we're the first according to the configured handshake order * - the caller explicitly requested that we do the handshake right now * - SSL isn't enabled on the other side * - SSL is forced on this side and *not* on the other (this means * that the other endpoint is using TLS and we usually cannot synchronize * forced SSL and TLS handshake because TLS depends on the client requesting * it) * - the other endpoint has already completed the SSL handshake */ if ((self->ssl_opts.handshake_seq != side) && !forced && self->ssl_opts.security[EP_OTHER(side)] > PROXY_SSL_SEC_NONE && !((self->ssl_opts.security[side] == PROXY_SSL_SEC_FORCE_SSL) && (self->ssl_opts.security[EP_OTHER(side)] != PROXY_SSL_SEC_FORCE_SSL)) && (self->ssl_opts.ssl_sessions[EP_OTHER(side)] == NULL)) { /* if we've requested a handshake, but the handshake order requires the other endpoint to be the first and that side isn't ready yet, we only register the intent */ z_proxy_log(self, CORE_DEBUG, 6, "Delaying SSL handshake after the other endpoint is ready; side='%s'", EP_STR(side)); self->ssl_opts.handshake_pending[side] = TRUE; z_proxy_return(self, TRUE); } /* at this point we're either the first side to do the handshake, or the other endpoint has already completed the handshake */ handshake = z_proxy_ssl_handshake_new(self, self->endpoints[side], side); rc = z_proxy_ssl_perform_handshake(handshake); if (!rc || !handshake->session) { z_proxy_return(self, rc); } if (self->ssl_opts.ssl_sessions[side]) z_proxy_ssl_clear_session(self, side); self->ssl_opts.ssl_sessions[side] = z_ssl_session_ref(handshake->session); if (side == EP_SERVER) z_proxy_ssl_register_host_iface(self); /* in case there's a pending handshake request on the other endpoint make sure we complete that */ side = EP_OTHER(side); if (self->ssl_opts.handshake_pending[side]) { z_proxy_log(self, CORE_DEBUG, 6, "Starting delayed SSL handshake; side='%s'", EP_STR(side)); g_assert(self->endpoints[side] != NULL); handshake = z_proxy_ssl_handshake_new(self, self->endpoints[side], side); self->ssl_opts.handshake_pending[side] = FALSE; rc = z_proxy_ssl_perform_handshake(handshake); if (self->ssl_opts.ssl_sessions[side]) z_proxy_ssl_clear_session(self, side); self->ssl_opts.ssl_sessions[side] = z_ssl_session_ref(handshake->session); if (side == EP_SERVER) z_proxy_ssl_register_host_iface(self); } z_proxy_return(self, rc); } /** * Clear SSL state on one of the proxy endpoints. * * @param self the proxy instance * @param side the side being cleared * * This function cleans up SSL state on one of the endpoints of the proxy. It takes care * of freeing the SSL session and unregistering the host interface on the server endpoint. * */ void z_proxy_ssl_clear_session(ZProxy *self, gint side) { z_proxy_enter(self); if (self->ssl_opts.ssl_sessions[side]) { if (side == EP_SERVER) { ZProxyHostIface *iface; iface = z_proxy_find_iface(self, Z_CLASS(ZProxyHostIface)); if (iface) { z_proxy_del_iface(self, iface); z_object_unref(&iface->super); } } z_ssl_session_unref(self->ssl_opts.ssl_sessions[side]); self->ssl_opts.ssl_sessions[side] = NULL; } z_proxy_leave(self); } /** * Tell the proxy core to force-connect the server endpoint if the handshake order requires it. * * @param self the proxy instance * @param val whether or not to force-connect * * Certain proxies (eg. HTTP) delay connecting the server endpoint until the request has * been processed. This makes using the (server, client) handshake order impossible. As * a workaround the proxy SSL core provides a way for the proxy to request the server * endpoint to be force-connected right upon proxy startup so that the server-side SSL * handshake can be completed before the client handshake. * * This function sets the knob enabling force-connecting the server endpoint. * */ void z_proxy_ssl_set_force_connect_at_handshake(ZProxy *self, gboolean val) { z_proxy_enter(self); /* force-connecting the server side is meaningful only if the configured * handshake order is server-client */ if (self->ssl_opts.handshake_seq == PROXY_SSL_HS_SERVER_CLIENT) self->ssl_opts.force_connect_at_handshake = val; z_proxy_leave(self); } zorp-3.9.5/lib/proxysslhostiface.c000066400000000000000000000143041172670260400172340ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Mincer * ***************************************************************************/ #include #include #include #include typedef struct _ZProxySslHostIface { ZProxyHostIface super; X509 *server_cert; gboolean hostname_checked; gboolean hostname_check_result; } ZProxySslHostIface; static gboolean z_proxy_ssl_host_iface_check_wildcard(ZProxy *s, const gchar *host_name, const gchar *pattern) { gchar **pattern_parts, **hostname_parts; gboolean success = FALSE; gint i; z_proxy_log(s, CORE_DEBUG, 6, "Checking certificate subject; host='%s', pattern='%s'", host_name, pattern); pattern_parts = g_strsplit(pattern, ".", 0); hostname_parts = g_strsplit(host_name, ".", 0); for (i = 0; pattern_parts[i]; i++) { if (!hostname_parts[i]) { /* number of dot separated entries is not the same in the hostname and the pattern spec */ goto exit; } if (!g_pattern_match_simple(pattern_parts[i], hostname_parts[i])) goto exit; } if (!hostname_parts[i]) /* if hostname_parts doesn't continue beyond pattern_parts */ success = TRUE; exit: g_strfreev(pattern_parts); g_strfreev(hostname_parts); if (!success) { z_proxy_log(s, CORE_VIOLATION, 2, "Certificate subject does not match; host='%s', pattern='%s'", host_name, pattern); } return success; } gboolean z_proxy_ssl_host_iface_check_name_method(ZProxyHostIface *s, const gchar *host_name, gchar *reason_buf, gsize reason_len) { ZProxySslHostIface *self = Z_CAST(s, ZProxySslHostIface); gint ext_ndx; gboolean found = FALSE, result = FALSE; gchar pattern_buf[256]; if (self->hostname_checked) return self->hostname_check_result; pattern_buf[0] = 0; ext_ndx = X509_get_ext_by_NID(self->server_cert, NID_subject_alt_name, -1); if (ext_ndx >= 0) { /* ok, there's a subjectAltName extension, check that */ X509_EXTENSION *ext; STACK_OF(GENERAL_NAME) *alt_names; GENERAL_NAME *gen_name; ext = X509_get_ext(self->server_cert, ext_ndx); alt_names = X509V3_EXT_d2i(ext); if (alt_names) { gint num, i; num = sk_GENERAL_NAME_num(alt_names); for (i = 0; i < num; i++) { gen_name = sk_GENERAL_NAME_value(alt_names, i); if (gen_name->type == GEN_DNS) { guchar *dnsname = ASN1_STRING_data(gen_name->d.dNSName); guint dnsname_len = ASN1_STRING_length(gen_name->d.dNSName); if (dnsname_len > sizeof(pattern_buf) - 1) { found = TRUE; result = FALSE; break; } memcpy(pattern_buf, dnsname, dnsname_len); pattern_buf[dnsname_len] = 0; /* we have found a DNS name as alternative subject name */ found = TRUE; result = z_proxy_ssl_host_iface_check_wildcard(s->owner, host_name, pattern_buf); break; } else if (gen_name->type == GEN_IPADD) { z_inet_ntoa(pattern_buf, sizeof(pattern_buf), *(struct in_addr *) gen_name->d.iPAddress->data); found = TRUE; result = strcmp(host_name, pattern_buf) == 0; break; } } sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); } } if (!found) { /* hmm. there was no subjectAltName (this is deprecated, but still * widely used), look up the Subject, most specific CN */ X509_NAME *name; name = X509_get_subject_name(self->server_cert); if (X509_NAME_get_text_by_NID(name, NID_commonName, pattern_buf, sizeof(pattern_buf)) != -1) { result = z_proxy_ssl_host_iface_check_wildcard(s->owner, host_name, pattern_buf); } } if (!result && reason_buf) { g_snprintf(reason_buf, reason_len, "Certificate does not belong to target host (certificate: %s, host %s)", pattern_buf, host_name); } self->hostname_checked = TRUE; self->hostname_check_result = result; return result; } ZProxyIface * z_proxy_ssl_host_iface_new(ZProxy *owner) { ZProxySslHostIface *self; self = Z_CAST(z_proxy_iface_new(Z_CLASS(ZProxySslHostIface), owner), ZProxySslHostIface); self->server_cert = owner->ssl_opts.peer_cert[EP_SERVER]; CRYPTO_add(&self->server_cert->references, 1, CRYPTO_LOCK_X509); return &self->super; } void z_proxy_ssl_host_iface_free_method(ZObject *s) { ZProxySslHostIface *self = Z_CAST(s, ZProxySslHostIface); X509_free(self->server_cert); z_proxy_iface_free_method(s); } ZProxyHostIfaceFuncs z_proxy_ssl_host_iface_funcs = { { Z_FUNCS_COUNT(ZProxyHostIface), z_proxy_ssl_host_iface_free_method, }, .check_name = z_proxy_ssl_host_iface_check_name_method, }; Z_CLASS_DEF(ZProxySslHostIface, ZProxyHostIface, z_proxy_ssl_host_iface_funcs); zorp-3.9.5/lib/proxystack.c000066400000000000000000000432321172670260400156540ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include /** * z_proxy_stack_prepare_streams: * @self: ZProxy instance * @downpair: * @uppair: * **/ static gboolean z_proxy_stack_prepare_streams(ZProxy *self, gint *downpair, gint *uppair) { z_proxy_enter(self); if (socketpair(AF_UNIX, SOCK_STREAM, 0, downpair) == -1) { /*LOG This message indicates that stacking a child proxy failed, because creating an AF_UNIX domain socketpair failed on the client side. */ z_proxy_log(self, CORE_ERROR, 1, "Error creating client socketpair for stacked proxy; error='%s'", g_strerror(errno)); z_proxy_leave(self); return FALSE; } else if (socketpair(AF_UNIX, SOCK_STREAM, 0, uppair) == -1) { close(downpair[0]); close(downpair[1]); /*LOG This message indicates that stacking a child proxy failed, because creating an AF_UNIX domain socketpair failed on the server side. */ z_proxy_log(self, CORE_ERROR, 1, "Error creating server socketpair for stacked proxy; error='%s'", g_strerror(errno)); z_proxy_leave(self); return FALSE; } z_proxy_leave(self); return TRUE; } /** * z_proxy_stack_proxy: * @self: proxy instance * @proxy_class: a Python class to be instantiated as the child proxy * * This function is called to start a child proxy. **/ gboolean z_proxy_stack_proxy(ZProxy *self, ZPolicyObj *proxy_class, ZStackedProxy **stacked, ZPolicyDict *stack_info) { int downpair[2], uppair[2]; ZPolicyObj *res, *client_stream, *server_stream, *stack_info_obj; ZStream *tmpstream; ZStream *client_upstream, *server_upstream; z_proxy_enter(self); if (proxy_class == z_policy_none) { z_policy_var_unref(proxy_class); z_proxy_leave(self); return FALSE; } if (!z_proxy_stack_prepare_streams(self, downpair, uppair)) { z_policy_var_unref(proxy_class); z_proxy_leave(self); return FALSE; } /*LOG This message reports that Zorp is about to stack a proxy class with the given fds as communication channels. */ z_proxy_log(self, CORE_DEBUG, 6, "Stacking subproxy; client='%d:%d', server='%d:%d'", downpair[0], downpair[1], uppair[0], uppair[1]); tmpstream = z_stream_fd_new(downpair[1], ""); client_stream = z_policy_stream_new(tmpstream); z_stream_unref(tmpstream); tmpstream = z_stream_fd_new(uppair[1], ""); server_stream = z_policy_stream_new(tmpstream); z_stream_unref(tmpstream); if (stack_info) { stack_info_obj = z_policy_struct_new(stack_info, Z_PST_SHARED); } else { stack_info_obj = z_policy_none_ref(); } res = z_policy_call(self->handler, "stackProxy", z_policy_var_build("(OOOO)", client_stream, server_stream, proxy_class, stack_info_obj), NULL, self->session_id); z_policy_var_unref(client_stream); z_policy_var_unref(server_stream); z_policy_var_unref(stack_info_obj); if (!res || res == z_policy_none || !z_policy_proxy_check(res)) { z_proxy_log(self, CORE_ERROR, 3, "Error stacking subproxy;"); close(downpair[0]); close(downpair[1]); close(uppair[0]); close(uppair[1]); z_policy_var_unref(res); z_proxy_leave(self); return FALSE; } client_upstream = z_stream_fd_new(downpair[0], ""); server_upstream = z_stream_fd_new(uppair[0], ""); *stacked = z_stacked_proxy_new(client_upstream, server_upstream, NULL, self, z_policy_proxy_get_proxy(res), 0); z_policy_var_unref(res); z_proxy_leave(self); return TRUE; } static gboolean z_proxy_stack_fds(ZProxy *self, gint client_fd, gint server_fd, gint control_fd, ZStackedProxy **stacked, guint32 flags) { ZStream *client_upstream, *server_upstream, *control_stream = NULL; z_proxy_enter(self); client_upstream = z_stream_fd_new(client_fd, ""); server_upstream = z_stream_fd_new(server_fd, ""); if (control_fd != -1) control_stream = z_stream_fd_new(control_fd, ""); *stacked = z_stacked_proxy_new(client_upstream, server_upstream, control_stream, self, NULL, flags); z_proxy_leave(self); return TRUE; } /** * z_proxy_control_stream_read: * @stream: stream to read from * @cond: I/O condition which triggered this call * @user_data: ZProxy instance as a generic pointer * * This function is registered as the read callback for control channels * of stacked programs. **/ static gboolean z_proxy_control_stream_read(ZStream *stream, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZStackedProxy *stacked = (ZStackedProxy *) user_data; ZProxy *proxy = stacked->proxy; GIOStatus st; gboolean success = FALSE; ZCPCommand *request = NULL, *response = NULL; ZCPHeader *hdr1, *hdr2; guint cp_sid; ZProxyIface *iface = NULL; const gchar *fail_reason = "Unknown reason"; gboolean result = TRUE; z_enter(); g_static_mutex_lock(&stacked->destroy_lock); if (stacked->destroyed) { /* NOTE: this stacked proxy has already been destroyed, but a callback was still pending, make sure we don't come back again. Note that our arguments except stacked might already be freed. */ result = FALSE; goto exit_unlock; } if (!stacked->control_proto) stacked->control_proto = z_cp_context_new(stream); st = z_cp_context_read(stacked->control_proto, &cp_sid, &request); if (st == G_IO_STATUS_AGAIN) goto exit_unlock; if (st != G_IO_STATUS_NORMAL) { /* FIXME: hack, returning FALSE should be enough but it causes the poll loop to spin, see bug #7219 */ z_stream_set_cond(stream, G_IO_IN, FALSE); result = FALSE; goto exit_unlock; } response = z_cp_command_new("RESULT"); if (cp_sid != 0) { fail_reason = "Non-zero session-id"; goto error; } z_log(NULL, CORE_DEBUG, 6, "Read request from stack-control channel; request='%s'", request->command->str); if (strcmp(request->command->str, "SETVERDICT") == 0 ) { ZProxyStackIface *siface; iface = z_proxy_find_iface(proxy, Z_CLASS(ZProxyStackIface)); if (!iface) { fail_reason = "Proxy does not support Stack interface"; goto error; } siface = (ZProxyBasicIface *) iface; if (strcmp(request->command->str, "SETVERDICT") == 0) { ZVerdict verdict; hdr1 = z_cp_command_find_header(request, "Verdict"); hdr2 = z_cp_command_find_header(request, "Description"); if (!hdr1) { fail_reason = "No Verdict header in SETVERDICT request"; goto error; } if (strcmp(hdr1->value->str, "Z_ACCEPT") == 0) verdict = ZV_ACCEPT; else if (strcmp(hdr1->value->str, "Z_REJECT") == 0) verdict = ZV_REJECT; else if (strcmp(hdr1->value->str, "Z_DROP") == 0) verdict = ZV_DROP; else if (strcmp(hdr1->value->str, "Z_ERROR") == 0) verdict = ZV_ERROR; else verdict = ZV_UNSPEC; z_proxy_stack_iface_set_verdict(siface, verdict, hdr2 ? hdr2->value->str : NULL); } } else { fail_reason = "Unknown request received"; goto error; } success = TRUE; error: z_cp_command_add_header(response, g_string_new("Status"), g_string_new(success ? "OK" : "Failure"), FALSE); if (!success) { z_cp_command_add_header(response, g_string_new("Fail-Reason"), g_string_new(fail_reason), FALSE); z_log(NULL, CORE_DEBUG, 6, "Error processing control channel request; request='%s', reason='%s'", request ? request->command->str : "None", fail_reason); } z_log(NULL, CORE_DEBUG, 6, "Responding on stack-control channel; response='%s'", response->command->str); if (z_cp_context_write(stacked->control_proto, 0, response) != G_IO_STATUS_NORMAL) { /* this should not have happened */ z_log(NULL, CORE_ERROR, 1, "Internal error writing response to stack-control channel;"); success = FALSE; } if (iface) z_object_unref(&iface->super); if (request) z_cp_command_free(request); if (response) z_cp_command_free(response); exit_unlock: g_static_mutex_unlock(&stacked->destroy_lock); z_return(result); } /** * z_proxy_stack_program: * @self: proxy instance * @program: path to program to execute * * This function is called to start a program as a filtering child proxy. **/ static gboolean z_proxy_stack_program(ZProxy *self, const gchar *program, ZStackedProxy **stacked) { int downpair[2], uppair[2], controlpair[2]; pid_t pid; z_proxy_enter(self); if (!z_proxy_stack_prepare_streams(self, downpair, uppair)) { z_proxy_leave(self); return FALSE; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, controlpair) < 0) { close(downpair[0]); close(downpair[1]); close(uppair[0]); close(uppair[1]); close(controlpair[0]); close(controlpair[1]); /*LOG This message indicates that stacking a child proxy failed, because creating an AF_UNIX domain socketpair failed for the control channel. */ z_proxy_log(self, CORE_ERROR, 1, "Error creating control socketpair for stacked proxy; error='%s'", g_strerror(errno)); z_proxy_leave(self); return FALSE; } /*LOG This message reports that Zorp is about to stack a program with the given fds as communication channels. */ z_proxy_log(self, CORE_DEBUG, 6, "Stacking program; client='%d:%d', server='%d:%d', control='%d:%d', program='%s'", downpair[0], downpair[1], uppair[0], uppair[1], controlpair[0], controlpair[1], program); pid = fork(); if (pid == 0) { int i; /* child */ dup2(downpair[1], 0); dup2(uppair[1], 1); /* standard error is inherited */ dup2(controlpair[1], 3); for (i = 4; i < sysconf(_SC_OPEN_MAX); i++) close(i); execl("/bin/sh", "/bin/sh", "-c", program, NULL); fprintf(stderr, "Error starting program; program='%s', error='%s'", program, strerror(errno)); exit(127); } else if (pid < 0) { z_proxy_log(self, CORE_ERROR, 2, "Program stacking failed, fork returned error; program='%s', error='%s'", program, g_strerror(errno)); close(downpair[0]); close(downpair[1]); close(uppair[0]); close(uppair[1]); close(controlpair[0]); close(controlpair[1]); z_proxy_leave(self); return FALSE; } close(downpair[1]); close(uppair[1]); close(controlpair[1]); if (!z_proxy_stack_fds(self, downpair[0], uppair[0], controlpair[0], stacked, 0)) { z_proxy_leave(self); return FALSE; } z_proxy_leave(self); return TRUE; } static gboolean z_proxy_stack_tuple(ZProxy *self, ZPolicyObj *tuple, ZStackedProxy **stacked, ZPolicyDict *stack_info_dict) { guint stack_method; ZPolicyObj *arg = NULL; gboolean success = FALSE; if (!z_policy_tuple_get_verdict(tuple, &stack_method) || z_policy_seq_length(tuple) < 2) goto invalid_tuple; arg = z_policy_seq_getitem(tuple, 1); switch (stack_method) { case Z_STACK_PROXY: if (z_policy_seq_length(tuple) != 2) goto invalid_tuple; success = z_proxy_stack_proxy(self, arg, stacked, stack_info_dict); break; case Z_STACK_PROGRAM: if (!z_policy_str_check(arg)) goto invalid_tuple; success = z_proxy_stack_program(self, z_policy_str_as_string(arg), stacked); break; default: break; } exit: if (arg) z_policy_var_unref(arg); return success; invalid_tuple: z_proxy_log(self, CORE_POLICY, 1, "Invalid stack tuple;"); success = FALSE; goto exit; } /** * z_proxy_stack_object: * @self: ZProxy instance * @stack_obj: Python object to be stacked * * This function is a more general interface than * z_proxy_stack_proxy/z_proxy_stack_object it first decides how the * specified Python object needs to be stacked, performs stacking and * returns the stacked proxy. **/ gboolean z_proxy_stack_object(ZProxy *self, ZPolicyObj *stack_obj, ZStackedProxy **stacked, ZPolicyDict *stack_info) { *stacked = NULL; if (z_policy_str_check(stack_obj)) return z_proxy_stack_program(self, z_policy_str_as_string(stack_obj), stacked); else if (z_policy_seq_check(stack_obj)) return z_proxy_stack_tuple(self, stack_obj, stacked, stack_info); else return z_proxy_stack_proxy(self, stack_obj, stacked, stack_info); } /* stacked proxy */ static inline ZStackedProxy * z_stacked_proxy_ref(ZStackedProxy *self) { z_refcount_inc(&self->ref_cnt); return self; } static void z_stacked_proxy_unref(ZStackedProxy *self) { if (self && z_refcount_dec(&self->ref_cnt)) { g_free(self); } } /** * z_stacked_proxy_new: * @client_stream: client side stream * @server_stream: server side stream * @control_stream: control stream * @proxy: ZProxy instance which initiated stacking * @child_proxy: ZProxy instance of the 'child' proxy * * This function creates a new ZStackedProxy instance encapsulating * information about a stacked proxy instance. This information can be freed * by calling z_stacked_proxy_destroy(). It consumes the stream references * passed to it (client, server) but does not consume the proxy * references (@proxy and @child_proxy) **/ ZStackedProxy * z_stacked_proxy_new(ZStream *client_stream, ZStream *server_stream, ZStream *control_stream G_GNUC_UNUSED, ZProxy *proxy, ZProxy *child_proxy, guint32 flags) { ZStackedProxy *self = g_new0(ZStackedProxy, 1); gchar buf[Z_STREAM_MAX_NAME]; z_proxy_enter(proxy); z_refcount_set(&self->ref_cnt, 1); self->flags = flags; if (client_stream) { z_stream_set_nonblock(client_stream, TRUE); g_snprintf(buf, sizeof(buf), "%s/client_downstream", proxy->session_id); z_stream_set_name(client_stream, buf); self->downstreams[EP_CLIENT] = client_stream; } if (server_stream) { z_stream_set_nonblock(server_stream, TRUE); g_snprintf(buf, sizeof(buf), "%s/server_downstream", proxy->session_id); z_stream_set_name(server_stream, buf); self->downstreams[EP_SERVER] = server_stream; } self->proxy = z_proxy_ref(proxy); if (child_proxy) self->child_proxy = z_proxy_ref(child_proxy); if (control_stream) { g_snprintf(buf, sizeof(buf), "%s/control", proxy->session_id); z_stream_set_name(control_stream, buf); self->control_stream = z_stream_push(z_stream_push(control_stream, z_stream_line_new(NULL, 4096, ZRL_EOL_NL|ZRL_TRUNCATE)), z_stream_buf_new(NULL, 4096, Z_SBF_IMMED_FLUSH)); z_stream_set_callback(self->control_stream, G_IO_IN, z_proxy_control_stream_read, z_stacked_proxy_ref(self), (GDestroyNotify) z_stacked_proxy_unref); z_stream_set_cond(self->control_stream, G_IO_IN, TRUE); /* NOTE: this has to be called after complete initialization of * this instance as the callback might be called before * ZStackedProxy is actually initialized */ z_stream_attach_source(self->control_stream, NULL); } z_proxy_leave(proxy); return self; } /** * z_stacked_proxy_destroy: * @self: ZStackedProxy instance * * This function frees all references associated with a stacked proxy. **/ void z_stacked_proxy_destroy(ZStackedProxy *self) { gint i; z_enter(); g_static_mutex_lock(&self->destroy_lock); self->destroyed = TRUE; if (self->control_stream) { z_stream_detach_source(self->control_stream); z_stream_shutdown(self->control_stream, SHUT_RDWR, NULL); z_stream_close(self->control_stream, NULL); z_stream_unref(self->control_stream); self->control_stream = NULL; } /* no callbacks after this point, thus the control stream callback * does not need a reference */ for (i = 0; i < EP_MAX; i++) { if (self->downstreams[i]) { z_stream_shutdown(self->downstreams[i], SHUT_RDWR, NULL); z_stream_close(self->downstreams[i], NULL); z_stream_unref(self->downstreams[i]); self->downstreams[i] = NULL; } } if (self->child_proxy) { z_proxy_del_child(self->proxy, self->child_proxy); z_proxy_unref(self->child_proxy); self->child_proxy = NULL; } if (self->proxy) { z_proxy_unref(self->proxy); self->proxy = NULL; } g_static_mutex_unlock(&self->destroy_lock); z_stacked_proxy_unref(self); z_return(); } zorp-3.9.5/lib/pyattach.c000066400000000000000000000200341172670260400152550ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include /* * struct ZPolicyAttach * * Author: Bazsi, 2000/03/27 * Purpose: this class encapsulates a connector * */ typedef struct _ZPolicyAttach { PyObject_HEAD ZPolicy *policy; ZAttach *attach; ZSockAddr *local; } ZPolicyAttach; static PyTypeObject z_policy_attach_type; /** * z_policy_attach_block_method: * @self this * @args not used * * Zorp.Attach.block, block execution until the connection gets established. * (Wrapper around z_attach_block.) Note that if there is a callback installed * (@handler of the constructor), then this method will fail. * * Returns: * NULL when there was a callback installed * PyNone if an error happened during connecting * The new data stream (Zorp.Stream) otherwise */ static PyObject * z_policy_attach_start_method(ZPolicyAttach *self, PyObject *args G_GNUC_UNUSED) { PyObject *res; ZConnection *conn; gboolean success; z_enter(); Py_BEGIN_ALLOW_THREADS success = z_attach_start_block(self->attach, &conn); Py_END_ALLOW_THREADS if (success && conn) { /* NOTE: we don't assign a name to this stream now, it will be assigned later */ res = z_policy_stream_new(conn->stream); self->local = z_sockaddr_ref(conn->local); z_connection_destroy(conn, FALSE); } else { res = z_policy_none_ref(); } z_return(res); } static PyMethodDef z_policy_attach_methods[] = { { "start", (PyCFunction) z_policy_attach_start_method, 0, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; /** * z_policy_attach_getattr: * @o: this * @name: Attribute name * * Get the value of an attribute * Currently only the methods and one attribute is supported: 'local', the * address of the local endpoint. * * Returns: * The attribute value as a Python object */ static PyObject * z_policy_attach_getattr(PyObject *o, char *name) { ZPolicyAttach *self = (ZPolicyAttach *) o; PyObject *back; z_enter(); if (strcmp(name, "local") == 0) { if (self->local) { back = z_policy_sockaddr_new(self->local); z_return(back); } else { z_return(z_policy_none_ref()); } } else { back = Py_FindMethod(z_policy_attach_methods, o, name); z_return(back); } } /** * z_policy_attach_new_instance: * @s not used * @args Python args: proxy, protocol, local, remote, handler * * Constructor of Zorp.Attach. After creating and setting up a new instance, * creates self->attach (ZAttach), passing the arguments to its constructor * z_attach_new. The argument @handler will be called when the connection * got established. * * Returns: * The new instance */ static PyObject * z_policy_attach_new_instance(PyObject *s G_GNUC_UNUSED, PyObject *args, PyObject *keywords) { ZPolicyAttach *self; PyObject *local, *remote; PyObject *fake_args, *proxy_instance; ZAttachParams params; static gchar *tcp_keywords[] = { "timeout", "local_loose", "tos", "local_random", NULL }; static gchar *udp_keywords[] = { "timeout", "local_loose", "tos", "local_random", NULL }; gchar buf1[MAX_SOCKADDR_STRING], buf2[MAX_SOCKADDR_STRING]; ZSockAddr *local_sa, *remote_sa; guint protocol; z_enter(); /* called by python, no need to lock the interpreter */ if (!PyArg_ParseTuple(args, "OiOO", &proxy_instance, &protocol, &local, &remote)) z_return(NULL); if (!z_policy_proxy_check(proxy_instance)) { PyErr_SetString(PyExc_TypeError, "First argument must be a Proxy instance"); z_return(NULL); } if (((local != z_policy_none) && !z_policy_sockaddr_check(local)) || (!z_policy_sockaddr_check(remote))) { PyErr_SetString(PyExc_TypeError, "Local and remote arguments must be SockAddr or None"); z_return(NULL); } memset(¶ms, 0, sizeof(params)); params.tos = -1; params.timeout = 30000; fake_args = PyTuple_New(0); switch (protocol) { case ZD_PROTO_TCP: if (!PyArg_ParseTupleAndKeywords(fake_args, keywords, "|iiii", tcp_keywords, ¶ms.timeout, ¶ms.loose, ¶ms.tos, ¶ms.random)) { Py_XDECREF(fake_args); z_return(NULL); } break; case ZD_PROTO_UDP: if (!PyArg_ParseTupleAndKeywords(fake_args, keywords, "|iiii", udp_keywords, ¶ms.timeout, ¶ms.loose, ¶ms.tos, ¶ms.random)) { Py_XDECREF(fake_args); z_return(NULL); } break; } Py_XDECREF(fake_args); self = PyObject_New(ZPolicyAttach, &z_policy_attach_type); if (!self) { z_return(NULL); } local_sa = (local == z_policy_none) ? NULL : z_policy_sockaddr_get_sa(local); remote_sa = z_policy_sockaddr_get_sa(remote); /*LOG This message indicates that Zorp began establishing connection with the indicated remote host. */ z_log(z_policy_proxy_get_proxy(proxy_instance)->session_id, CORE_DEBUG, 7, "Connecting to remote host; protocol='%d', local='%s', remote='%s'", protocol, local_sa ? z_sockaddr_format(local_sa, buf1, sizeof(buf1)) : "NULL", z_sockaddr_format(remote_sa, buf2, sizeof(buf2))); self->local = NULL; self->policy = NULL; self->attach = z_attach_new(z_policy_proxy_get_proxy(proxy_instance), protocol, local_sa, remote_sa, ¶ms, NULL, NULL, NULL); z_sockaddr_unref(remote_sa); z_sockaddr_unref(local_sa); if (!self->attach) { PyErr_SetString(PyExc_IOError, "Error during connect"); Py_XDECREF(self); z_return(NULL); } self->policy = z_policy_ref(current_policy); z_leave(); return (PyObject *) self; } /** * z_policy_attach_free: * @self: this * * Destructor for Zorp.Attach, called automatically to free up this instance */ static void z_policy_attach_free(ZPolicyAttach *self) { z_enter(); if (self->attach) { z_attach_free(self->attach); } if (self->policy) { z_policy_unref(self->policy); } z_sockaddr_unref(self->local); PyObject_Del(self); z_leave(); } PyMethodDef z_policy_attach_funcs[] = { { "Attach", (PyCFunction) z_policy_attach_new_instance, METH_VARARGS | METH_KEYWORDS, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; static PyTypeObject z_policy_attach_type = { PyVarObject_HEAD_INIT(&PyType_Type,0) .tp_name = "ZPolicyAttach", .tp_basicsize = sizeof(ZPolicyAttach), .tp_dealloc = (destructor) z_policy_attach_free, .tp_getattr = (getattrfunc) z_policy_attach_getattr, .tp_doc = "ZPolicyAttach class for Zorp" }; /** * z_policy_attach_init: * * Module initialisation */ void z_policy_attach_module_init(void) { Py_InitModule("Zorp.Zorp", z_policy_attach_funcs); } zorp-3.9.5/lib/pyaudit.c000066400000000000000000000023321172670260400151200ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Panther * Auditor : * Last audited version: * Notes: * ***************************************************************************/ zorp-3.9.5/lib/pybalance.c000066400000000000000000000000001172670260400153650ustar00rootroot00000000000000zorp-3.9.5/lib/pycore.c000066400000000000000000000236431172670260400147520ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : kisza * Last audited version: 1.22 * Notes: * ***************************************************************************/ /* * this module implements the interface with python */ #include #include #include #include #include #include #include #include #include #include #include #include PyObject *PyExc_LicenseException; /* exported python methods */ /** * z_py_log: * @self: Python self argument * @args: Python args tuple * * Called by Python to send a message to the event log. There are several * alternative invocations of this function: * * def log(class, verbosity, msg) * def log(session_id, class, verbosity, logformat, args) * * Returns: Py_None **/ static PyObject * z_py_log(PyObject *self G_GNUC_UNUSED, PyObject *args) { unsigned long verbosity; char *class, *msg; PyObject *py_session_id, *log_fmt, *log_args, *log_msg = NULL; gchar *session_id; if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "args must be a tuple"); return NULL; } if (PyTuple_Size(args) == 3) { if (!PyArg_ParseTuple(args, "sis", &class, &verbosity, &msg)) return NULL; session_id = NULL; } else { log_args = NULL; if (!PyArg_ParseTuple(args, "OsiO|O", &py_session_id, &class, &verbosity, &log_fmt, &log_args)) return NULL; if (!z_log_enabled(class, verbosity)) { return z_policy_none_ref(); } /* convert session ID */ if (py_session_id == Py_None) { session_id = NULL; } else if (PyString_Check(py_session_id)) { session_id = PyString_AsString(py_session_id); } else { PyErr_SetString(PyExc_TypeError, "Session ID must be string or None"); return NULL; } if (!PyString_Check(log_fmt)) { PyErr_SetString(PyExc_TypeError, "Format must be string"); return NULL; } if (log_args != NULL) { log_msg = PyString_Format(log_fmt, log_args); if (!log_msg) { return NULL; } msg = PyString_AsString(log_msg); } else { msg = PyString_AsString(log_fmt); } } /*NOLOG*/ z_log(session_id, class, verbosity, "%s", msg); Py_XDECREF(log_msg); return z_policy_none_ref(); } /*+ +*/ static PyObject * z_py_quit(PyObject *self G_GNUC_UNUSED, PyObject *args) { int exit_code; z_enter(); if (!PyArg_ParseTuple(args, "i", &exit_code)) z_return(NULL); z_main_loop_quit(exit_code); z_return(z_policy_none_ref()); } static PyObject * z_py_stream_pair(PyObject *self G_GNUC_UNUSED, PyObject *args) { int domain, type, proto = 0; int result[2]; ZStream *streams[2]; PyObject *pystreams[2], *res; z_enter(); if (!PyArg_ParseTuple(args, "ii|i", &domain, &type, &proto)) z_return(NULL); if (socketpair(domain, type, proto, result) == -1) { PyErr_SetString(PyExc_IOError, "I/O error during socketpair."); z_return(NULL); } streams[0] = z_stream_fd_new(result[0], "streamPair/A"); streams[1] = z_stream_fd_new(result[1], "streamPair/B"); pystreams[0] = z_policy_stream_new(streams[0]); pystreams[1] = z_policy_stream_new(streams[1]); z_stream_unref(streams[0]); z_stream_unref(streams[1]); res = z_policy_var_build("(OO)", pystreams[0], pystreams[1]); z_policy_var_unref(pystreams[0]); z_policy_var_unref(pystreams[1]); z_return(res); } static PyObject * z_py_get_instance_id(PyObject *self G_GNUC_UNUSED, PyObject *args) { static GHashTable *instance_ids = NULL; gint *value; gchar *service_name; if (!PyArg_Parse(args, "(s)", &service_name)) return NULL; if (instance_ids == NULL) instance_ids = g_hash_table_new(g_str_hash, g_str_equal); value = g_hash_table_lookup(instance_ids, service_name); if (!value) { value = g_new(gint, 1); *value = 0; g_hash_table_insert(instance_ids, g_strdup(service_name), value); } else { (*value)++; } return PyInt_FromLong(*value); } static PyObject * z_py_szig_event(PyObject *self G_GNUC_UNUSED, PyObject *args) { gint event; guint type; PyObject *value, *value_repr; ZSzigValue *sv; GTimeVal tv; z_enter(); if (!PyArg_Parse(args, "(iO)", &event, &value) || !PyArg_Parse(value, "(iO)", &type, &value_repr)) z_return(NULL); switch (type) { case Z_SZIG_TYPE_LONG: if (!PyInt_Check(value_repr)) { PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_LONG requires an integer argument"); z_return(NULL); } sv = z_szig_value_new_long(PyInt_AsLong(value_repr)); break; case Z_SZIG_TYPE_TIME: if (!PyArg_Parse(value_repr, "(ii)", &tv.tv_sec, &tv.tv_usec)) z_return(NULL); sv = z_szig_value_new_time(&tv); break; case Z_SZIG_TYPE_STRING: if (!PyString_Check(value_repr)) { PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_STRING requires a string argument"); z_return(NULL); } sv = z_szig_value_new_string(PyString_AsString(value_repr)); break; case Z_SZIG_TYPE_PROPS: { gchar *name; PyObject *dict; PyObject *key, *value; Z_PYTHON_SIZE_TYPE i; if (!PyArg_Parse(value_repr, "(sO)", &name, &dict)) z_return(NULL); if (!PyDict_Check(dict)) { PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_PROPS requires a mapping as 2nd argument"); z_return(NULL); } sv = z_szig_value_new_props(name, NULL); i = 0; while (PyDict_Next(dict, &i, &key, &value)) { if (PyString_Check(key)) { if (PyString_Check(value)) { z_szig_value_add_prop(sv, PyString_AsString(key), z_szig_value_new_string(PyString_AsString(value))); } else if (PyInt_Check(value)) { z_szig_value_add_prop(sv, PyString_AsString(key), z_szig_value_new_long(PyInt_AsLong(value))); } else { z_szig_value_free(sv, TRUE); PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_PROPS requires a string->string or string->int mapping"); z_return(NULL); } } else { z_szig_value_free(sv, TRUE); PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_PROPS cannot handle not string keys"); z_return(NULL); } } } break; case Z_SZIG_TYPE_CONNECTION_PROPS: { gchar *service; gint instance_id, sec_conn_id, related_id; PyObject *dict; PyObject *key, *value; Z_PYTHON_SIZE_TYPE i; if (!PyArg_Parse(value_repr, "(siiiO)", &service, &instance_id, &sec_conn_id, &related_id, &dict)) z_return(NULL); if (!PyDict_Check(dict)) { PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_CONNECTION_PROPS requires a mapping as 5th argument"); z_return(NULL); } sv = z_szig_value_new_connection_props(service, instance_id, sec_conn_id, related_id, NULL); i = 0; while (PyDict_Next(dict, &i, &key, &value)) { if (!PyString_Check(key) || !PyString_Check(value)) { z_szig_value_free(sv, TRUE); PyErr_SetString(PyExc_ValueError, "Z_SZIG_TYPE_CONNECTION_PROPS requires a string->string mapping"); z_return(NULL); } z_szig_value_add_connection_prop(sv, PyString_AsString(key), PyString_AsString(value)); } } break; default: PyErr_SetString(PyExc_ValueError, "Unknown SZIG type;"); z_return(NULL); } z_szig_event(event, sv); z_return(z_policy_none_ref()); } /** * **/ static PyObject * z_policy_notify_event(PyObject *self G_GNUC_UNUSED, PyObject *args G_GNUC_UNUSED ) { return z_policy_none_ref(); } static PyMethodDef zorp_funcs[] = { { "log", z_py_log, METH_VARARGS, NULL }, { "quit", z_py_quit, METH_VARARGS, NULL }, { "streamPair", z_py_stream_pair, METH_VARARGS, NULL }, { "getInstanceId", z_py_get_instance_id, METH_VARARGS, NULL }, { "szigEvent", z_py_szig_event, METH_VARARGS, NULL }, { "notifyEvent", z_policy_notify_event, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } }; void z_py_zorp_core_init(void) { Py_InitModule("Zorp.Zorp", zorp_funcs); } zorp-3.9.5/lib/pydict.c000066400000000000000000001371371172670260400147510ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include /** * Description * * The ZPolicyDict object is a glue between C variables and types and * their corresponding Python counterparts. It is basically equivalent to * a Python dictionary. * * A dictionary is a mapping of (variable name -> C storage description), * where storage description is usually a type (one of Z_VT_*) and a * pointer. The primary user of this interface is pystruct which is a * generic Python type that uses ZPolicyDict as its instance dictionary. * Simple types like integers and strings are constructed when the * variable of the given type is requested, more complex types (methods, * hashes etc) are constructed on demand, and a reference is cached for * future queries. * * As this code is quite closely related to Python, some of its functions * require the Python lock to be held. * * Destruction * * As there are circular references involved ZPolicyDict is destructed in * a two-phase destruction mechanism. ZPolicyDict instances are reference * counted, so in order to destroy an instance the reference count needs * to go to zero, application code usually has a single reference, * z_policy_dict_ref() is rarely needed in application code. This * "application" reference should be freed with a call to * z_policy_dict_destroy(). That function breaks circular references and * prepares the dictionary to be freed. If z_policy_dict_destroy() is not * called that dictionary _will_ be leaked. If z_policy_dict_destroy() is * called multiple times, an assertion will occur. * * Threading and locking * * It is assumed that a ZPolicyDict is constructed from a single thread, * thus variable registration is not protected by mutexes. The reference * counter is an atomic variable, so _ref and _unref can be called from * different threads. * **/ typedef struct _ZPolicyDictType ZPolicyDictType; typedef struct _ZPolicyDictEntry ZPolicyDictEntry; struct _ZPolicyDictType { void (*parse_args)(ZPolicyDict *self, ZPolicyDictEntry *entry, va_list args); ZPolicyObj *(*get_value)(ZPolicyDict *self, ZPolicyDictEntry *entry); gint (*set_value)(ZPolicyDict *self, ZPolicyDictEntry *entry, ZPolicyObj *new_value); void (*free_fn)(ZPolicyDictEntry *entry); }; /** * ZPolicyDictEntry: * * This structure represents an entry in the dictionary. **/ struct _ZPolicyDictEntry { ZPolicyDictType *type_funcs; ZVarType type; const gchar *name; guint flags; gpointer value; /* type specific internal storage, to avoid allocation when literals are used */ union { guint int_value; guint8 int8_value; guint16 int16_value; guint32 int32_value; guint64 int64_value; gsize cstring_buflen; ZPolicyObj *object_ref; struct { gpointer ptr; gchar *desc; } ptr; struct { ZPolicyObj *(*get_value)(gpointer user_data, const gchar *name, gpointer value); gint (*set_value)(gpointer user_data, const gchar *name, gpointer value, ZPolicyObj *new_value); void (*free_value)(gpointer value, gpointer user_data); gpointer user_data; GDestroyNotify user_data_free; } custom; struct { ZPolicyDictMethodFunc method; gpointer user_data; GDestroyNotify user_data_free; } method; struct { GHashTable *table; gboolean consume; } hash; struct { ZDimHashTable *table; gboolean consume; } dimhash; } ts; }; /** * z_policy_dict_entry_free: * @e: ZPolicyDictEntry instance * * Frees @e by calling the destructor function and freeing the structure itself. **/ static void z_policy_dict_entry_free(ZPolicyDictEntry *e) { if (e->flags & Z_VF_CONSUME) e->type_funcs->free_fn(e); g_free((gchar *) e->name); g_free(e); } /** * ZPolicyDict: * * An interface between C and Python, contains a mapping of attributes * stored in C variables, can be used as a getattr/setattr backend for * Python exported objects, but is not ZPolicyObj compatible on its * own */ struct _ZPolicyDict { /* ZPolicyObj that uses this dictionary as backend, used to implement alias lookup, borrowed reference to avoid circular references */ ZRefCount ref_cnt; ZPolicyObj *wrapper; /* hashtable that contains the registered variables */ GHashTable *vars; gpointer app_data; GDestroyNotify app_data_free; }; /* support functions for types above */ /****************************************************************************** * int attributes support * value points to a gint */ /** * z_policy_dict_int_parse_args: * @self: ZPolicyDict instance * @e: ZPolicyDictEntry being parsed * @args: argument list to parse **/ static void z_policy_dict_int_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_CONSUME)) == 0); if (e->flags & Z_VF_LITERAL) { switch (e->type) { case Z_VT_INT: e->value = &e->ts.int_value; e->ts.int_value = va_arg(args, gint); break; case Z_VT_INT8: e->value = &e->ts.int8_value; e->ts.int8_value = va_arg(args, gint); break; case Z_VT_INT16: e->value = &e->ts.int16_value; e->ts.int16_value = (guint16) va_arg(args, gint); break; case Z_VT_INT32: e->value = &e->ts.int32_value; e->ts.int32_value = va_arg(args, guint32); break; case Z_VT_INT64: e->value = &e->ts.int64_value; e->ts.int64_value = va_arg(args, guint64); break; default: g_assert_not_reached(); break; } } else { e->value = va_arg(args, gpointer); } } /** * z_policy_dict_int_get_value: * @self: not used * @name: not used * @value: (gint*) pointer to the value * * Gets the value of an integer variable * * Returns: * PyInt value */ static ZPolicyObj * z_policy_dict_int_get_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e) { ZPolicyObj *res; glong value; z_enter(); switch (e->type) { case Z_VT_INT: value = *(gint *) e->value; if (e->flags & Z_VF_INT_NET) value = ntohl(value); break; case Z_VT_INT8: value = *(guint8 *) e->value; break; case Z_VT_INT16: value = *(guint16 *) e->value; if (e->flags & Z_VF_INT_NET) value = ntohs(value); break; case Z_VT_INT32: value = *(guint32 *) e->value; if (e->flags & Z_VF_INT_NET) value = ntohl(value); break; case Z_VT_INT64: value = *(guint64 *) e->value; if (e->flags & Z_VF_INT_NET) value = GUINT64_FROM_BE(value); break; default: g_assert_not_reached(); break; } res = PyInt_FromLong(value); z_leave(); return res; } /** * z_policy_dict_int_set_value: * @self: not used * @name: not used * @value: (gint*) pointer to the value * @new: New value * * Sets the value of an integer variable * * Returns: * 0 on success, nonzero otherwise */ static gint z_policy_dict_int_set_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, ZPolicyObj *new) { gint value; z_enter(); if (!z_policy_var_parse(new, "i", &value)) z_return(1); switch (e->type) { case Z_VT_INT: if (e->flags & Z_VF_INT_NET) value = htonl(value); *((gint *) e->value) = value; break; case Z_VT_INT8: *((guint8 *) e->value) = value; break; case Z_VT_INT16: if (e->flags & Z_VF_INT_NET) value = htons(value); *((guint16 *) e->value) = value; break; case Z_VT_INT32: if (e->flags & Z_VF_INT_NET) value = htonl(value); *((guint32 *) e->value) = value; break; case Z_VT_INT64: if (e->flags & Z_VF_INT_NET) value = GUINT64_TO_BE(value); *((guint64 *) e->value) = value; break; default: g_assert_not_reached(); break; } z_leave(); return 0; } /****************************************************************************** * string attributes support (Z_VT_STRING and Z_VT_CSTRING * Z_VT_STRING * literal arguments: * const gchar * * non-literal arguments: * GString * * Z_VT_CSTRING: * literal arguments: * const gchar *, gsize (ignored unless ZF_VF_DUP is specified) * non-literal arguments * const gchar *, gsize */ /** * z_policy_dict_string_parse_args: * @self: ZPolicyDict instance * @e: ZPolicyDictEntry being parsed * @args: argument list to parse **/ static void z_policy_dict_string_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { switch (e->type) { case Z_VT_STRING: if (e->flags & Z_VF_LITERAL) { e->value = g_string_new(va_arg(args, gchar *)); e->flags |= Z_VF_CONSUME; } else { e->value = va_arg(args, gpointer); } break; case Z_VT_CSTRING: if (e->flags & Z_VF_LITERAL) { if (e->flags & Z_VF_DUP) { gchar *s; s = va_arg(args, gchar *); e->ts.cstring_buflen = va_arg(args, gsize); e->value = g_malloc(e->ts.cstring_buflen); g_strlcpy(e->value, s, e->ts.cstring_buflen); e->flags |= Z_VF_CONSUME; } else { g_assert((e->flags & (Z_VF_WRITE+Z_VF_CFG_WRITE)) == 0); e->value = va_arg(args, gchar *); va_arg(args, gsize); // pop size argument e->ts.cstring_buflen = strlen((gchar *) e->value); } } else { e->value = va_arg(args, gchar *); e->ts.cstring_buflen = va_arg(args, gsize); } break; default: g_assert_not_reached(); break; } } /** * z_policy_dict_string_get_value: * @self: not used * @name: not used * @value: (GString*) pointer to the value * * Gets the value of a GString variable * * Returns: * PyString value */ static ZPolicyObj * z_policy_dict_string_get_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e) { ZPolicyObj *res; z_enter(); switch (e->type) { case Z_VT_STRING: res = PyString_FromStringAndSize(((GString *) e->value)->str, ((GString *) e->value)->len); break; case Z_VT_CSTRING: res = PyString_FromString((gchar *) e->value); break; default: g_assert_not_reached(); break; } z_leave(); return res; } /** * z_policy_dict_string_set_value: * @self: not used * @name: not used * @value: (GString*) pointer to the value * @new: New value * * Sets the value of an integer variable * * Returns: * 0 on success, nonzero otherwise */ static gint z_policy_dict_string_set_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, ZPolicyObj *new) { gchar *str; guint len; z_enter(); if (!PyArg_Parse(new, "s#", &str, &len)) { z_leave(); return 1; } switch (e->type) { case Z_VT_STRING: g_string_assign((GString *) e->value, str); break; case Z_VT_CSTRING: g_strlcpy((gchar *) e->value, str, MIN(len + 1, e->ts.cstring_buflen)); break; default: g_assert_not_reached(); break; } z_leave(); return 0; } /** * z_policy_dict_string_free: * @value: (GString*) pointer to the value * * Deallocates a string variable */ static void z_policy_dict_string_free(ZPolicyDictEntry *e) { z_enter(); switch (e->type) { case Z_VT_STRING: g_string_free((GString *) e->value, TRUE); break; case Z_VT_CSTRING: g_free(e->value); break; default: g_assert_not_reached(); break; } z_leave(); } /****************************************************************************** * method attributes support * * Expected arguments: * func: C function pointer to call * user_data: argument passed to func * user_data_free (GDestroyNotify): function to free user_data with * * Represented as: * The value field contains a reference to the newly created */ typedef struct _ZPolicyMethod { PyObject_HEAD ZPolicyDict *dict; gpointer user_data; GDestroyNotify user_data_free; ZPolicyDictMethodFunc method; } ZPolicyMethod; static PyTypeObject z_policy_method_type; /** * z_policy_method_call: * @self: this * @args: Arguments to call the method with * @kw: not used * * Call the ZProxy method referred to by @self * * Returns: * The value returned by the method */ static ZPolicyObj * z_policy_method_call(ZPolicyMethod *self, ZPolicyObj *args, ZPolicyObj *kw) { return self->method(self->user_data, args, kw); } /** * z_policy_method_free: * @proxy: ZProxy instance * @method: Method function * * Constructor of ZorpMethod - a Python type that encapsulates a method of a class * derived from ZProxy * * Returns: * The new instance */ static ZPolicyObj * z_policy_method_new(ZPolicyDict *dict, ZPolicyDictMethodFunc method, gpointer user_data, GDestroyNotify user_data_free) { ZPolicyMethod *self; self = PyObject_New(ZPolicyMethod, &z_policy_method_type); if (!self) return NULL; self->user_data = user_data; self->user_data_free = user_data_free; self->method = method; self->dict = z_policy_dict_ref(dict); return (ZPolicyObj *) self; } /** * z_py_zorp_method_free: * @self: this * * Destructor of ZorpMethod */ static void z_policy_method_free(ZPolicyMethod *self) { if (self->user_data && self->user_data_free) self->user_data_free(self->user_data); z_policy_dict_unref(self->dict); PyObject_Del(self); } static PyTypeObject z_policy_method_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "ZPolicyMethod", .tp_basicsize = sizeof(ZPolicyMethod), .tp_dealloc = (destructor) z_policy_method_free, .tp_call = (ternaryfunc) z_policy_method_call, .tp_doc = "ZPolicyMethod class for Zorp" }; /** * z_policy_dict_method_parse_args: * @self: ZPolicyDict instance * @e: ZPolicyDictEntry being parsed * @args: argument list to parse **/ static void z_policy_dict_method_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_CONSUME+Z_VF_LITERAL)) == 0); e->flags |= Z_VF_CONSUME; e->ts.method.method = va_arg(args, ZPolicyDictMethodFunc); e->ts.method.user_data = va_arg(args, gpointer); e->ts.method.user_data_free = va_arg(args, gpointer); e->value = NULL; } /** * z_policy_dict_method_get_value: * @self: not used * @name: not used * @value: (PyObject*) pointer to the value * * Get the value of a ZorpMethod variable. * Note that this is not the same as _object_get(), since there the argument is * a pointer to a pointer to an object, while here only a casting to PyObject* is done. * * Returns: * The PyObject value */ static ZPolicyObj * z_policy_dict_method_get_value(ZPolicyDict *self, ZPolicyDictEntry *e) { ZPolicyObj *res; if (!e->value) { e->value = z_policy_method_new(self, e->ts.method.method, e->ts.method.user_data, e->ts.method.user_data_free); e->ts.method.user_data_free = NULL; } res = (ZPolicyObj *) e->value; z_policy_var_ref(res); return res; } /** * z_policy_dict_method_free: * @value: this * * Decrements the reference counter of the stored ZPolicyMethod instance. */ static void z_policy_dict_method_free(ZPolicyDictEntry *e) { if (e->value) { z_policy_var_unref((ZPolicyObj *) e->value); } else if (e->ts.method.user_data && e->ts.method.user_data_free) { e->ts.method.user_data_free(e->ts.method.user_data); e->ts.method.user_data = NULL; } } /****************************************************************************** * object attributes support */ /** * z_policy_dict_object_parse_args: * @self: ZPolicyDict instance * @e: ZPolicyDictEntry being parsed * @args: argument list to parse **/ static void z_policy_dict_object_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & Z_VF_DUP) == 0); if (e->flags & Z_VF_LITERAL) { e->value = &e->ts.object_ref; e->ts.object_ref = va_arg(args, ZPolicyObj *); } else { e->value = va_arg(args, ZPolicyObj **); } } /** * z_policy_dict_object_get_value: * @self: not used * @name: not used * @value: (ZPolicyObj**) pointer to the value * * Gets the value of a ZPolicyObj variable * * Returns: * The ZPolicyObj value */ static ZPolicyObj * z_policy_dict_object_get_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e) { ZPolicyObj *r; z_enter(); r = *((ZPolicyObj **) e->value); z_policy_var_ref(r); z_return(r); } /** * z_policy_dict_object_set_value: * @self: not used * @name: not used * @value: (ZPolicyObj**) pointer to the value * @new: New value * * Sets the value of a ZPolicyObj variable * * Returns: * 1 (always succeeds) */ static gint z_policy_dict_object_set_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, ZPolicyObj *new_value) { z_enter(); z_policy_var_unref(*(ZPolicyObj **) e->value); *((ZPolicyObj **) e->value) = new_value; z_policy_var_ref(new_value); z_return(0); } /** * z_policy_dict_object_free: * @value: (ZPolicyObj**) pointer to the value * * Decrements the reference counter of a ZPolicyObj variable */ static void z_policy_dict_object_free(ZPolicyDictEntry *e) { z_enter(); z_policy_var_unref(*((ZPolicyObj **) e->value)); z_return(); } /****************************************************************************** * ip/ipv6 address attributes support */ /** * z_policy_dict_object_parse_args: * @self: ZPolicyDict instance * @e: ZPolicyDictEntry being parsed * @args: argument list to parse **/ static void z_policy_dict_ip_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_LITERAL)) == 0); e->value = va_arg(args, gpointer); } /** * z_policy_dict_object_get_value: * @self: not used * @name: not used * @value: (ZPolicyObj**) pointer to the value * * Gets the value of a ZPolicyObj variable * * Returns: * The ZPolicyObj value */ static ZPolicyObj * z_policy_dict_ip_get_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e) { ZPolicyObj *res; z_enter(); if (e->flags & Z_VF_IP_STR) { gchar buf[64]; inet_ntop(e->type == Z_VT_IP ? AF_INET : AF_INET6, e->value, buf, sizeof(buf)); res = PyString_FromString(buf); } else if (e->type == Z_VT_IP) { res = PyInt_FromLong(((struct in_addr *) e->value)->s_addr); } else { struct in6_addr *in6 = (struct in6_addr *) e->value; res = Py_BuildValue("(iiiiiiii)", in6->s6_addr16[0], in6->s6_addr16[1], in6->s6_addr16[2], in6->s6_addr16[3], in6->s6_addr16[4], in6->s6_addr16[5], in6->s6_addr16[6], in6->s6_addr16[7]); } z_return(res); } /** * z_policy_dict_object_set_value: * @self: not used * @name: not used * @value: (ZPolicyObj**) pointer to the value * @new: New value * * Sets the value of a ZPolicyObj variable * * Returns: * 0 on success, nonzero otherwise */ static gint z_policy_dict_ip_set_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, ZPolicyObj *new_value) { z_enter(); if (e->flags & Z_VF_IP_STR) { gchar *ip; if (!PyArg_Parse(new_value, "s", &ip)) z_return(1); inet_pton(e->type == Z_VT_IP ? AF_INET : AF_INET6, ip, e->value); } else { switch (e->type) { case Z_VT_IP6: { struct in6_addr *in6 = (struct in6_addr *) e->value; if (!PyArg_Parse(new_value, "(iiiiiiii)", &in6->s6_addr16[0], &in6->s6_addr16[1], &in6->s6_addr16[2], &in6->s6_addr16[3], &in6->s6_addr16[4], &in6->s6_addr16[5], &in6->s6_addr16[6], &in6->s6_addr16[7])) return 1; break; } case Z_VT_IP: { struct in_addr *in = (struct in_addr *) e->value; if (!PyArg_Parse(new_value, "I", &in->s_addr)) return 1; break; } default: g_assert_not_reached(); break; } } z_leave(); return 0; } /** * z_policy_dict_object_free: * @value: (ZPolicyObj**) pointer to the value * * Decrements the reference counter of a ZPolicyObj variable */ static void z_policy_dict_ip_free(ZPolicyDictEntry *e) { z_enter(); g_free(e->value); z_return(); } /****************************************************************************** * alias support */ static void z_policy_dict_alias_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_CONSUME+Z_VF_LITERAL)) == 0); e->value = va_arg(args, gchar *); } /** * z_policy_dict_alias_get_value: * @self: this * @name: not used * @value: Name of the alias * * Gets the real name of an alias-variable * * Returns: * The real name of the variable */ static ZPolicyObj * z_policy_dict_alias_get_value(ZPolicyDict *self, ZPolicyDictEntry *e) { g_assert(self->wrapper); return PyObject_GetAttrString(self->wrapper, (gchar *) e->value); } /** * z_policy_dict_alias_set_value: * @self: this * @name: not used * @value: Name of the alias * @new: Name of the real variable * * Sets the alias @value to refer to @new * * Returns: * ??? */ static gint z_policy_dict_alias_set_value(ZPolicyDict *self, ZPolicyDictEntry *e, ZPolicyObj *new_value) { g_assert(self->wrapper); return PyObject_SetAttrString(self->wrapper, (gchar *) e->value, new_value); } /****************************************************************************** * hash-table attributes support */ /* * Python compatible object, representing a hash of ZPolicyObj instances. */ typedef struct _ZPolicyHash { PyObject_HEAD ZPolicyDict *dict; gboolean consume; GHashTable *hash; } ZPolicyHash; extern PyTypeObject z_policy_hash_type; /** * z_policy_hash_subscript: * @self: this * @k: PyString key * * Performs a lookup for @k in the hash table. * * Returns: * The value if found, otherwise NULL */ static ZPolicyObj * z_policy_hash_subscript(ZPolicyHash *self, ZPolicyObj *k) { gchar *key; ZPolicyObj *res; if (!PyArg_Parse(k, "s", &key)) return NULL; res = g_hash_table_lookup(self->hash, key); if (res) { z_policy_var_ref(res); return res; } else { PyErr_SetObject(PyExc_KeyError, k); return NULL; } } /** * z_policy_hash_ass_subscript: * @self: this * @u: PyString key * @v: ZPolicyObj value * * Insert-or-update the value @v for the key @u in the hash table. * If there was a previous value for the key, its reference counter * is decremented. * * Returns: * 0 on success, -1 otherwise */ static gint z_policy_hash_ass_subscript(ZPolicyHash *self, ZPolicyObj *u, ZPolicyObj *v) { gchar *key; ZPolicyObj *res; if (!PyArg_Parse(u, "s", &key)) return -1; res = g_hash_table_lookup(self->hash, key); if (v == NULL) { /* delete item */ if (!res) { PyErr_SetObject(PyExc_KeyError, u); return -1; } g_hash_table_remove(self->hash, key); z_policy_var_unref(res); return 0; } else { z_policy_var_ref(v); g_hash_table_insert(self->hash, key, v); z_policy_var_unref(res); return 0; } } /** * z_policy_hash_new: * @hash: GHashTable containing the items to represent * * Constructor of ZPolicyHash - the Python type that encapsulates a * glib hash table containing ZPolicyObj items. * * Returns: * */ static ZPolicyHash * z_policy_hash_new(ZPolicyDict *dict, GHashTable *hash, gboolean consume) { ZPolicyHash *self = PyObject_New(ZPolicyHash, &z_policy_hash_type); self->hash = hash; self->consume = consume; self->dict = z_policy_dict_ref(dict); return self; } /** * z_policy_hash_unref_items: * @key: not used * @value: this * @user_data: not used * * Helper function for _free, decrements the reference counter of the objects * in the hashtable. * * Returns: * */ static gboolean z_policy_hash_unref_items(gpointer key G_GNUC_UNUSED, gpointer value, gpointer user_data G_GNUC_UNUSED) { z_policy_var_unref((ZPolicyObj *) value); return TRUE; } static void z_policy_hash_destroy_table(GHashTable *hash) { g_hash_table_foreach_remove(hash, z_policy_hash_unref_items, NULL); g_hash_table_destroy(hash); } /** * z_policy_hash_free: * @self: this * * Destructor of ZPolicyHash. Removes all items in the hash, and the hash itself, too. */ static void z_policy_hash_free(ZPolicyHash *self) { if (self->consume) z_policy_hash_destroy_table(self->hash); z_policy_dict_unref(self->dict); PyObject_Del(self); } PyMappingMethods z_policy_hash_mapping = { NULL, (binaryfunc) z_policy_hash_subscript, (objobjargproc) z_policy_hash_ass_subscript }; PyTypeObject z_policy_hash_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp hash", .tp_basicsize = sizeof(ZPolicyHash), .tp_dealloc = (destructor) z_policy_hash_free, .tp_as_mapping = &z_policy_hash_mapping, .tp_doc = "ZPolicyHash class for Zorp" }; void z_policy_dict_hash_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_LITERAL)) == 0); e->ts.hash.consume = !!(e->flags & Z_VF_CONSUME); e->flags |= Z_VF_CONSUME; e->ts.hash.table = va_arg(args, GHashTable *); e->value = NULL; } /** * z_policy_dict_hash_get: * @self: not used * @name: not used * @value: (ZPolicyHash*) pointer to the value * * Get the value to a ZPolicyHash variable - cast to ZPolicyObj, just like at * z_policy_dict_method_get. * * Returns: * The ZPolicyHash value */ static ZPolicyObj * z_policy_dict_hash_get_value(ZPolicyDict *self, ZPolicyDictEntry *e) { ZPolicyObj *res; if (!e->value) e->value = z_policy_hash_new(self, e->ts.hash.table, e->ts.hash.consume); res = (ZPolicyObj *) e->value; z_policy_var_ref(res); return res; } /** * z_policy_dict_hash_free: * @value: this * * Destructor of ZPolicyHash */ static void z_policy_dict_hash_free(ZPolicyDictEntry *e) { if (e->value) { z_policy_var_unref((ZPolicyObj *) e->value); } else if (e->ts.hash.consume) { z_policy_hash_destroy_table(e->ts.hash.table); } } /****************************************************************************** * Multidimensional hash table attributes support */ typedef struct _ZPolicyDimHash { PyObject_HEAD ZPolicyDict *dict; gboolean consume; ZDimHashTable *hash; } ZPolicyDimHash; extern PyTypeObject z_policy_dim_hash_type; /** * z_policy_dim_hash_unref_items: * @value: this * * Decrements the reference counter * * Returns: * TRUE */ static gboolean z_policy_dim_hash_unref_items(gpointer value) { z_policy_var_unref((ZPolicyObj *) value); return TRUE; } /** * z_policy_dim_hash_subscript: * @self: this * @k: key sequence * * Assembles a composite key ("key0::key1::...:keyN") from the sequence @k and * looks up its value from the hash. * * Returns: * The value if found, otherwise NULL */ static ZPolicyObj * z_policy_dim_hash_subscript(ZPolicyDimHash *self, ZPolicyObj *k) { gchar **keys; gchar *key; guint keynum; ZPolicyObj *item; ZPolicyObj *stritem; ZPolicyObj *res; guint i; if (PyArg_Parse(k, "s", &key)) { keynum = 1; keys = g_new0(gchar *, keynum); keys[0] = g_strdup(key); } else { PyErr_Clear(); if (z_policy_seq_check(k)) { keynum = z_policy_seq_length(k); keys = g_new0(gchar *, keynum); for (i = 0; i < keynum; i++) { item = z_policy_seq_getitem(k, i); stritem = z_policy_var_str(item); z_policy_var_unref(item); key = z_policy_str_as_string(stritem); keys[i] = g_new0(gchar, strlen(key)+1); strcpy(keys[i], key); z_policy_var_unref(stritem); } } else return NULL; } res = z_dim_hash_table_lookup(self->hash, keynum, keys); z_dim_hash_key_free(keynum, keys); if (res) { z_policy_var_ref(res); return res; } else { PyErr_SetObject(PyExc_KeyError, k); return NULL; } } /** * z_policy_dim_hash_ass_subscript: * @self: this * @u: key sequence * @v: new value * * Assemble a composite key from the sequence @u, and assign @v to it * in the hash, creating a new entry if the key was a new one, or replacing * the previous value if it wasn't. * * Returns: * 0 on success, -1 on error */ static gint z_policy_dim_hash_ass_subscript(ZPolicyDimHash *self, ZPolicyObj *u, ZPolicyObj *v) { gchar **keys; gchar *key; guint keynum; ZPolicyObj *res; ZPolicyObj *item; ZPolicyObj *stritem; guint i; if (PyArg_Parse(u, "s", &key)) { keynum = 1; keys = g_new0(gchar *, keynum); keys[0] = g_new0(gchar, strlen(key)+1); strcpy(keys[0], key); } else { PyErr_Clear(); if (z_policy_seq_check(u)) { keynum = z_policy_seq_length(u); keys = g_new0(gchar *, keynum); for (i = 0; i < keynum; i++) { item = z_policy_seq_getitem(u, i); stritem = z_policy_var_str(item); z_policy_var_unref(item); key = z_policy_str_as_string(stritem); keys[i] = g_new0(gchar, strlen(key)+1); strcpy(keys[i], key); z_policy_var_unref(stritem); } } else return -1; } res = z_dim_hash_table_lookup(self->hash, keynum, keys); if (v == NULL) { /* delete item */ if (!res) { PyErr_SetObject(PyExc_KeyError, u); z_dim_hash_key_free(keynum, keys); return -1; } z_dim_hash_table_delete(self->hash, keynum, keys, z_policy_dim_hash_unref_items); z_dim_hash_key_free(keynum, keys); return 0; } else { if (res) z_dim_hash_table_delete(self->hash, keynum, keys, z_policy_dim_hash_unref_items); z_policy_var_ref(v); z_dim_hash_table_insert(self->hash, v, keynum, keys); z_dim_hash_key_free(keynum, keys); return 0; } } /** * z_policy_dim_hash_new: * @hash: ZDimHashTable to create a ZPolicyDimHash around * * Constructor of ZPolicyDimHash - a Python class that encapsulates a * ZDimHashTable. * * Returns: * The new instance */ static ZPolicyDimHash * z_policy_dim_hash_new(ZPolicyDict *dict, ZDimHashTable *hash, gboolean consume) { ZPolicyDimHash *self = PyObject_New(ZPolicyDimHash, &z_policy_dim_hash_type); self->dict = z_policy_dict_ref(dict); self->hash = hash; self->consume = consume; return self; } /** * z_policy_dim_hash_free: * @self: this * * Destructor of ZPolicyDimHash */ static void z_policy_dim_hash_free(ZPolicyDimHash *self) { if (self->consume) z_dim_hash_table_free(self->hash, z_policy_dim_hash_unref_items); z_policy_dict_unref(self->dict); PyObject_Del(self); } PyMappingMethods z_policy_dim_hash_mapping = { NULL, (binaryfunc) z_policy_dim_hash_subscript, (objobjargproc) z_policy_dim_hash_ass_subscript }; PyTypeObject z_policy_dim_hash_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp Multidimensional hash", .tp_basicsize = sizeof(ZPolicyDimHash), .tp_dealloc = (destructor) z_policy_dim_hash_free, .tp_as_mapping = &z_policy_dim_hash_mapping, .tp_doc = "ZPolicyDimHash class for Zorp" }; static void z_policy_dict_dimhash_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_LITERAL)) == 0); e->ts.dimhash.consume = !!(e->flags & Z_VF_CONSUME); e->flags |= Z_VF_CONSUME; e->ts.dimhash.table = va_arg(args, ZDimHashTable *); e->value = NULL; } /** * z_policy_dict_dimhash_get_value: * @self: not used * @name: not used * @value: (ZPolicyDimHash*) pointer to the value * * Get the value to a ZPolicyDimHash variable - cast to ZPolicyObj. * * Returns: * The value */ static ZPolicyObj * z_policy_dict_dimhash_get_value(ZPolicyDict *self, ZPolicyDictEntry *e) { ZPolicyObj *res; if (!e->value) e->value = z_policy_dim_hash_new(self, e->ts.dimhash.table, e->ts.dimhash.consume); res = (ZPolicyObj *) e->value; z_policy_var_ref(res); return res; } /** * z_policy_dict_dimhash_free: * @value: this * * Free a dimhash value in a a ZPolicyDict */ static void z_policy_dict_dimhash_free(ZPolicyDictEntry *e) { if (e->value) z_policy_var_unref((ZPolicyObj *) e->value); else if (e->ts.dimhash.consume) z_dim_hash_table_free(e->ts.dimhash.table, z_policy_dim_hash_unref_items); } /****************************************************************************** * custom attributes */ static void z_policy_dict_custom_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { e->flags |= Z_VF_CONSUME; e->value = va_arg(args, gpointer); e->ts.custom.get_value = va_arg(args, gpointer); e->ts.custom.set_value = va_arg(args, gpointer); e->ts.custom.free_value = va_arg(args, gpointer); e->ts.custom.user_data = va_arg(args, gpointer); e->ts.custom.user_data_free = va_arg(args, GDestroyNotify); } /** * z_policy_dict_custom_get_value: * @self: not used * @name: not used * @value: * * Returns: * The value */ static ZPolicyObj * z_policy_dict_custom_get_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e) { return e->ts.custom.get_value(e->ts.custom.user_data, e->name, e->value); } /** * z_policy_dict_custom_set_value: * @self: not used * @name: not used * @value: * * Returns: */ static gint z_policy_dict_custom_set_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, ZPolicyObj *new_value) { return e->ts.custom.set_value(e->ts.custom.user_data, e->name, e->value, new_value); } static void z_policy_dict_custom_free(ZPolicyDictEntry *e) { if (e->ts.custom.free_value) e->ts.custom.free_value(e->value, e->ts.custom.user_data); if (e->ts.custom.user_data && e->ts.custom.user_data_free) e->ts.custom.user_data_free(e->ts.custom.user_data); } /****************************************************************************** * pointer attributes support * value is a gpointer */ /** * z_policy_dict_ptr_parse_args: * @self: ZPolicyDict instance * @e: ZPolicyDictEntry being parsed * @args: argument list to parse **/ static void z_policy_dict_ptr_parse_args(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e, va_list args) { g_assert((e->flags & (Z_VF_DUP+Z_VF_CONSUME)) == 0); if (e->flags & Z_VF_LITERAL) { e->value = &e->ts.ptr.ptr; e->ts.ptr.ptr = va_arg(args, gpointer); } else { e->value = va_arg(args, gpointer *); } e->ts.ptr.desc = va_arg(args, gchar *); } /** * z_policy_dict_ptr_get_value: * @self: not used * @name: not used * @value: (gint*) pointer to the value * * Gets the value of an integer variable * * Returns: * PyInt value */ static ZPolicyObj * z_policy_dict_ptr_get_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e) { ZPolicyObj *res; z_enter(); /* FIXME: we could support properly reference counted values here, though * nothing would use them for now */ res = PyCObject_FromVoidPtrAndDesc(*(gpointer *) e->value, e->ts.ptr.desc, NULL); z_return(res); } /** * z_policy_dict_ptr_set_value: * @self: not used * @name: not used * @value: (gint*) pointer to the value * @new: New value * * Sets the value of an integer variable * * Returns: * 0 on success, nonzero otherwise */ static gint z_policy_dict_ptr_set_value(ZPolicyDict *self G_GNUC_UNUSED, ZPolicyDictEntry *e G_GNUC_UNUSED, ZPolicyObj *new G_GNUC_UNUSED) { return 0; } /****************************************************************************** * ZPolicyDict core */ /** * z_policy_dict_wrap: * @self: ZPolicyDict instance * @wrapper: a Python object that uses @self as its dictionary * * This function stores a borrowed reference to @wrapper in @self. This * means that ZPolicyDict assumes that the destruction of @wrapper * automatically destroys @self as well as no further references are * possible after that point, as self->wrapper will become stale. * * This borrowed reference is used to resolve variable aliases. **/ void z_policy_dict_wrap(ZPolicyDict *self, ZPolicyObj *wrapper) { self->wrapper = wrapper; } /** * z_policy_dict_unwrap: * @self: ZPolicyDict instance * @wrapper: wrapper to unwrap from * * This function unassigns @wrapper from this dictionary. It is useful when * the same dictionary is attached to another Python object. **/ void z_policy_dict_unwrap(ZPolicyDict *self, ZPolicyObj *wrapper) { g_assert(self->wrapper == wrapper); self->wrapper = NULL; } /** * z_policy_dict_get_value: * @self: ZPolicyDict instance * @is_config: whether this variable query is at config time or not * @name: name of the variable * * This function can be used to "get" a value from this dictionary, and is * used by various Python extension types (like ZPolicyStruct or ZProxy) to * implement their "getattr" method. **/ ZPolicyObj * z_policy_dict_get_value(ZPolicyDict *self, gboolean is_config, const gchar *name) { ZPolicyDictEntry *e; e = g_hash_table_lookup(self->vars, name); if (e) { if ((is_config && (e->flags & Z_VF_CFG_READ)) || (!is_config && (e->flags & Z_VF_READ))) { if (e->flags & Z_VF_OBSOLETE) { z_log(NULL, CORE_POLICY, 3, "Fetching obsolete attribute; name='%s'", name); } return e->type_funcs->get_value(self, e); } else { z_log(NULL, CORE_POLICY, 3, "Attribute cannot be read; config='%d', name='%s'", is_config, name); } } return NULL; } /** * z_policy_dict_set_value: * @self: ZPolicyDict instance * @is_config: whether this variable query is at config time or not * @name: name of the variable * @new_value: new value of the variable * * This function can be used to "set" a value in this dictionary. It is * used by various Python extension types (like ZPolicyStruct or ZProxy) to * implement their "setattr" method. **/ gint z_policy_dict_set_value(ZPolicyDict *self, gboolean is_config, const gchar *name, ZPolicyObj *new_value) { ZPolicyDictEntry *e; e = g_hash_table_lookup(self->vars, name); if (e) { if ((is_config && (e->flags & Z_VF_CFG_WRITE)) || (!is_config && (e->flags & Z_VF_WRITE))) { if (e->flags & Z_VF_OBSOLETE) { z_log(NULL, CORE_POLICY, 3, "Changing obsolete attribute; name='%s'", name); } return e->type_funcs->set_value(self, e, new_value); } else { z_log(NULL, CORE_POLICY, 3, "Attribute cannot be written; config='%d', name='%s'", is_config, name); return -1; } } return 1; } /** * z_policy_dict_insert_values: * @key: * @entry: * @user_data: * * This function is used to iterate through the dictionary hash and generate * a Python dictionary with the results. It is used to implement the * "__dict__" hash of the object. **/ static void z_policy_dict_insert_values(gpointer key, gpointer entry G_GNUC_UNUSED, gpointer user_data) { gpointer *params = (gpointer *) user_data; ZPolicyDict *self = params[0]; ZPolicyObj *dict = params[1]; ZPolicyObj *value; value = z_policy_dict_get_value(self, FALSE, (gchar *) key); PyDict_SetItemString(dict, (gchar *) key, value); z_policy_var_unref(value); } /** * z_policy_dict_get_dict: * @self: ZPolicyDict instance * * This function constructs and returns a Python dictionary object which * represents the dictionary. It can be used to peek into the dictionary * internals using dir(object). **/ ZPolicyObj * z_policy_dict_get_dict(ZPolicyDict *self) { ZPolicyObj *dict, *proxy_dict; gpointer params[2]; dict = PyDict_New(); params[0] = self; params[1] = dict; g_hash_table_foreach(self->vars, z_policy_dict_insert_values, params); proxy_dict = PyDictProxy_New(dict); z_policy_var_unref(dict); return proxy_dict; } ZPolicyDictType z_policy_dict_types[] = { [Z_VT_NONE] = { NULL, NULL, NULL, NULL }, [Z_VT_INT] = { z_policy_dict_int_parse_args, z_policy_dict_int_get_value, z_policy_dict_int_set_value, NULL }, [Z_VT_INT8] = { z_policy_dict_int_parse_args, z_policy_dict_int_get_value, z_policy_dict_int_set_value, NULL }, [Z_VT_INT16] = { z_policy_dict_int_parse_args, z_policy_dict_int_get_value, z_policy_dict_int_set_value, NULL }, [Z_VT_INT32] = { z_policy_dict_int_parse_args, z_policy_dict_int_get_value, z_policy_dict_int_set_value, NULL }, [Z_VT_INT64] = { z_policy_dict_int_parse_args, z_policy_dict_int_get_value, z_policy_dict_int_set_value, NULL }, [Z_VT_STRING] = { z_policy_dict_string_parse_args, z_policy_dict_string_get_value, z_policy_dict_string_set_value, z_policy_dict_string_free }, [Z_VT_CSTRING] = { z_policy_dict_string_parse_args, z_policy_dict_string_get_value, z_policy_dict_string_set_value, z_policy_dict_string_free }, [Z_VT_METHOD] = { z_policy_dict_method_parse_args, z_policy_dict_method_get_value, NULL, z_policy_dict_method_free }, [Z_VT_OBJECT] = { z_policy_dict_object_parse_args, z_policy_dict_object_get_value, z_policy_dict_object_set_value, z_policy_dict_object_free }, [Z_VT_IP] = { z_policy_dict_ip_parse_args, z_policy_dict_ip_get_value, z_policy_dict_ip_set_value, z_policy_dict_ip_free }, [Z_VT_IP6] = { z_policy_dict_ip_parse_args, z_policy_dict_ip_get_value, z_policy_dict_ip_set_value, z_policy_dict_ip_free }, [Z_VT_ALIAS] = { z_policy_dict_alias_parse_args, z_policy_dict_alias_get_value, z_policy_dict_alias_set_value, NULL }, [Z_VT_HASH] = { z_policy_dict_hash_parse_args, z_policy_dict_hash_get_value, NULL, z_policy_dict_hash_free }, [Z_VT_DIMHASH] = { z_policy_dict_dimhash_parse_args, z_policy_dict_dimhash_get_value, NULL, z_policy_dict_dimhash_free }, [Z_VT_CUSTOM] = { z_policy_dict_custom_parse_args, z_policy_dict_custom_get_value, z_policy_dict_custom_set_value, z_policy_dict_custom_free }, [Z_VT_PTR] = { z_policy_dict_ptr_parse_args, z_policy_dict_ptr_get_value, z_policy_dict_ptr_set_value, NULL }, }; static void z_policy_dict_register_va(ZPolicyDict *self, ZVarType var_type, va_list args) { ZPolicyDictEntry *e; va_list args_copy; g_assert((guint) var_type < sizeof(z_policy_dict_types) / sizeof(z_policy_dict_types[0])); e = g_new0(ZPolicyDictEntry, 1); e->name = g_strdup(va_arg(args, gchar *)); e->flags = va_arg(args, guint); e->type = var_type; e->type_funcs = &z_policy_dict_types[var_type]; g_assert((e->flags & (Z_VF_WRITE+Z_VF_CFG_WRITE)) == 0 || e->type_funcs->set_value); g_assert((e->flags & (Z_VF_READ+Z_VF_CFG_READ)) == 0 || e->type_funcs->get_value); va_copy(args_copy, args); e->type_funcs->parse_args(self, e, args_copy); va_end(args_copy); g_hash_table_insert(self->vars, (gchar *) e->name, e); } /** * z_policy_dict_register: * @self: ZPolicyDict instance * @var_type: type of the variable to register * * This is a vararg function that implements registering variables in the * dictionary. The type of the variable determines the remaining arguments. * See the type specific parse_args function for more information. **/ void z_policy_dict_register(ZPolicyDict *self, ZVarType var_type, ...) { va_list args; va_start(args, var_type); z_policy_dict_register_va(self, var_type, args); va_end(args); } /** * z_policy_dict_set_app_data: * @self: ZPolicyDict instance * @data: user_data pointer to be associated with @self * @data_free: destroy notify callback for @data * * Each ZPolicyDict instance has an associated "application data", * particularly useful when variables reference memory areas within some * kind of data structure directly. The application data will not be freed * as long as any data references can be made through the dictionary. **/ void z_policy_dict_set_app_data(ZPolicyDict *self, gpointer data, GDestroyNotify data_free) { g_assert(self->app_data == NULL); self->app_data = data; self->app_data_free = data_free; } /** * z_policy_dict_get_app_data: * @self: ZPolicyDict instance * * Return the current application data pointer. **/ gpointer z_policy_dict_get_app_data(ZPolicyDict *self) { return self->app_data; } static void z_policy_dict_call_iter(gpointer key, gpointer value G_GNUC_UNUSED, gpointer user_data) { gpointer *args = user_data; ZPolicyDict *self = args[0]; ZPolicyDictIterFunc iter = args[1]; iter(self, (const gchar *) key, args[2]); } void z_policy_dict_iterate(ZPolicyDict *self, ZPolicyDictIterFunc iter, gpointer user_data) { gpointer args[3] = { self, iter, user_data }; g_hash_table_foreach(self->vars, z_policy_dict_call_iter, args); } /** * z_policy_dict_new: * * ZPolicyDict constructor, prepares the dictionary for variable registrations. **/ ZPolicyDict * z_policy_dict_new(void) { ZPolicyDict *self = g_new0(ZPolicyDict, 1); z_refcount_set(&self->ref_cnt, 1); self->vars = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) z_policy_dict_entry_free); return self; } /** * z_policy_dict_ref: * @self: ZPolicyDict instance * * Increase the reference counter for @self. **/ ZPolicyDict * z_policy_dict_ref(ZPolicyDict *self) { z_refcount_inc(&self->ref_cnt); return self; } /** * z_policy_dict_unref: * @self: ZPolicyDict instance * * Decrement the reference counter for @self and free the data structure if * it reaches zero. **/ void z_policy_dict_unref(ZPolicyDict *self) { /* NOTE: requires the python state to be locked... */ if (self && z_refcount_dec(&self->ref_cnt)) { if (self->app_data && self->app_data_free) { self->app_data_free(self->app_data); } g_free(self); } } /** * z_policy_dict_destroy: * @self: ZPolicyDict instance * * Start disposing the dictionary by breaking possible circular references. * This function must be called exactly once for each dictionary. See the * notes in the top of the file for more information. **/ void z_policy_dict_destroy(ZPolicyDict *self) { g_assert(self->vars); g_hash_table_destroy(self->vars); self->vars = NULL; z_policy_dict_unref(self); } zorp-3.9.5/lib/pydispatch.c000066400000000000000000000502741172670260400156210ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include /* ZPolicyDispatchBind */ ZDispatchBind * z_policy_dispatch_bind_get_db(ZPolicyObj *self) { ZDispatchBind *db; if (!z_policy_dispatch_bind_check(self)) return NULL; db = (ZDispatchBind *) z_policy_dict_get_app_data(z_policy_struct_get_dict(self)); return z_dispatch_bind_ref(db); } ZPolicyObj * z_policy_dispatch_format(ZPolicyObj *s) { ZPolicyObj *res = NULL; ZDispatchBind *bind = z_policy_dispatch_bind_get_db(s); char buf[MAX_SOCKADDR_STRING]; g_assert(bind != NULL); switch(bind->type) { case ZD_BIND_SOCKADDR: res = PyString_FromFormat("SockAddrInet(%s)", z_sockaddr_format(bind->sa.addr, buf, sizeof(buf))); break; case ZD_BIND_IFACE: res = PyString_FromFormat("DBIface(iface=%s, port=%d)", bind->iface.iface, bind->iface.port); break; case ZD_BIND_IFACE_GROUP: res = PyString_FromFormat("DBIfaceGroup(group=%d, port=%d)", bind->iface_group.group, bind->iface_group.port); break; default: g_assert_not_reached(); break; } z_dispatch_bind_unref(bind); return res; } static ZPolicyObj * z_policy_dispatch_bind_format(gpointer user_data, ZPolicyObj *args, ZPolicyObj *kw G_GNUC_UNUSED) { ZDispatchBind *bind = (ZDispatchBind *) user_data; char buf[MAX_SOCKADDR_STRING]; if (!z_policy_var_parse(args, "()")) return NULL; return PyString_FromString(z_dispatch_bind_format(bind, buf, sizeof(buf))); } static ZPolicyObj * z_policy_dispatch_bind_new(ZDispatchBind *bind) { ZPolicyDict *dict; gint struct_type; ZPolicyObj *res; dict = z_policy_dict_new(); z_policy_dict_register(dict, Z_VT_INT16, "protocol", Z_VF_RW, &bind->protocol); z_policy_dict_register(dict, Z_VT_INT, "type", Z_VF_READ, &bind->type); z_policy_dict_register(dict, Z_VT_METHOD, "format", Z_VF_READ, z_policy_dispatch_bind_format, bind, NULL); switch (bind->type) { case ZD_BIND_SOCKADDR: z_policy_dict_register(dict, Z_VT_OBJECT, "sa", Z_VF_RW + Z_VF_LITERAL + Z_VF_CONSUME, z_policy_sockaddr_new(bind->sa.addr)); struct_type = Z_PST_DB_SOCKADDR; break; case ZD_BIND_IFACE: z_policy_dict_register(dict, Z_VT_CSTRING, "iface", Z_VF_RW, &bind->iface.iface, sizeof(bind->iface.iface)); z_policy_dict_register(dict, Z_VT_INT16, "port", Z_VF_RW, &bind->iface.port); z_policy_dict_register(dict, Z_VT_IP, "ip", Z_VF_RW, &bind->iface.ip4); z_policy_dict_register(dict, Z_VT_IP, "ip_s", Z_VF_RW | Z_VF_IP_STR, &bind->iface.ip4); struct_type = Z_PST_DB_IFACE; break; case ZD_BIND_IFACE_GROUP: z_policy_dict_register(dict, Z_VT_INT32, "group", Z_VF_RW, &bind->iface_group.group); z_policy_dict_register(dict, Z_VT_INT16, "port", Z_VF_RW, &bind->iface_group.port); struct_type = Z_PST_DB_IFACE_GROUP; break; default: g_assert_not_reached(); break; } z_dispatch_bind_ref(bind); z_policy_dict_set_app_data(dict, bind, (GDestroyNotify) z_dispatch_bind_unref); res = z_policy_struct_new(dict, struct_type); z_policy_struct_set_format(res, z_policy_dispatch_format); return res; } static ZPolicyObj * z_policy_dispatch_bind_new_instance_sa(ZPolicyObj *self G_GNUC_UNUSED, ZPolicyObj *args, ZPolicyObj *kw_args) { gchar *keywords[] = { "sa", "protocol", NULL }; ZDispatchBind *bind; ZPolicyObj *policy_sa, *res; ZSockAddr *sa; guint protocol = ZD_PROTO_AUTO; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "O|i", keywords, &policy_sa, &protocol)) { return NULL; } if (!z_policy_sockaddr_check(policy_sa)) { PyErr_SetString(PyExc_ValueError, "Expected SockAddr"); return NULL; } sa = z_policy_sockaddr_get_sa(policy_sa); bind = z_dispatch_bind_new_sa(protocol, sa); z_sockaddr_unref(sa); res = z_policy_dispatch_bind_new(bind); z_dispatch_bind_unref(bind); return res; } static ZPolicyObj * z_policy_dispatch_bind_new_instance_iface(ZPolicyObj *self G_GNUC_UNUSED, ZPolicyObj *args, ZPolicyObj *kw_args) { gchar *keywords[] = { "iface", "port", "family", "protocol", "ip", NULL }; ZDispatchBind *bind; ZPolicyObj *res; const gchar *iface = NULL, *ip = "0.0.0.0"; guint protocol = ZD_PROTO_AUTO, port = 0, family = AF_INET; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "si|iis", keywords, &iface, &port, &family, &protocol, &ip)) { return NULL; } if (port == 0) { PyErr_SetString(PyExc_ValueError, "Interface bound dispatches require a non-zero port"); return NULL; } bind = z_dispatch_bind_new_iface(protocol, iface, family, ip, port); res = z_policy_dispatch_bind_new(bind); z_dispatch_bind_unref(bind); return res; } static ZPolicyObj * z_policy_dispatch_bind_new_instance_iface_group(ZPolicyObj *self G_GNUC_UNUSED, ZPolicyObj *args, ZPolicyObj *kw_args) { gchar *keywords[] = { "group", "port", "family", "protocol", NULL }; ZDispatchBind *bind; ZPolicyObj *res, *group_obj; guint group = 0; guint protocol = ZD_PROTO_AUTO, port = 0, family = AF_INET; if (!PyArg_ParseTupleAndKeywords(args, kw_args, "Oi|ii", keywords, &group_obj, &port, &family, &protocol)) { return NULL; } if (z_policy_str_check(group_obj)) { FILE *ifgroups; gchar *group_name, *end; group_name = z_policy_str_as_string(group_obj); group = strtoul(group_name, &end, 0); if (*end != 0) { group = 0; ifgroups = fopen("/etc/iproute2/rt_ifgroup", "r"); if (ifgroups) { guint value; gchar name[32]; gchar buf[256]; while (fgets(buf, sizeof(buf), ifgroups)) { if (buf[0] == '#' || buf[0] == '\n' || buf[0] == 0) continue; if (sscanf(buf, "%x %32s\n", &value, name) == 2) { if (strcmp(name, group_name) == 0) { group = value; break; } } } fclose(ifgroups); } } if (!group) { PyErr_SetString(PyExc_RuntimeError, "Error resolving interface group name"); return NULL; } } else if (PyInt_Check(group_obj)) { group = PyInt_AsLong(group_obj); } if (port == 0) { PyErr_SetString(PyExc_ValueError, "Interface Group bound dispatches require a non-zero port"); return NULL; } bind = z_dispatch_bind_new_iface_group(protocol, group, family, port); res = z_policy_dispatch_bind_new(bind); z_dispatch_bind_unref(bind); return res; } /* ZPolicyDispatch class */ typedef struct _ZPolicyDispatch { PyObject_HEAD ZPolicy *policy; ZPolicyThread *policy_thread; ZDispatchEntry *dispatch; gboolean threaded; PyObject *handler; } ZPolicyDispatch; static PyTypeObject z_policy_dispatch_type; static PyMethodDef z_policy_dispatch_methods[]; /** * z_policy_dispatch_accept: * @conn The new incoming connection * @user_data this * * Internal callback, will be registered as the callback of ZDispatchEntry * instances in the constructor of ZPolicyDispatch. This function will be called * on new incoming connections, passes the connection to self->handler, which * will end up in the 'accepted' method of AbstractDispatch. * * Called by the main thread, so it locks using the global python state * * Returns: TRUE */ static gboolean z_policy_dispatch_accept(ZConnection *conn, gpointer user_data) { ZPolicyDispatch *self = (ZPolicyDispatch *) user_data; PyObject *res, *addr, *local, *pystream, *bound; z_enter(); z_policy_thread_acquire(self->policy_thread); if (conn) { ZSockAddr *tmpsa; /* NOTE: we cloning sockaddrs here as ref/unref on sockaddrs is not * reentrant, thus it is wise to use separate copies in each thread */ tmpsa = z_sockaddr_clone(conn->dest, FALSE); local = z_policy_sockaddr_new(tmpsa); z_sockaddr_unref(tmpsa); tmpsa = z_sockaddr_clone(conn->remote, FALSE); addr = z_policy_sockaddr_new(tmpsa); z_sockaddr_unref(tmpsa); bound = z_policy_dispatch_bind_new(conn->dispatch_bind); pystream = z_policy_stream_new(conn->stream); } else { local = z_policy_none_ref(); addr = z_policy_none_ref(); bound = z_policy_none_ref(); pystream = z_policy_none_ref(); } res = PyEval_CallFunction(self->handler, "(OOOO)", pystream, addr, local, bound); Py_XDECREF(bound); Py_XDECREF(addr); Py_XDECREF(local); Py_XDECREF(pystream); /* once python was called we assume that it takes care about the fd * we just passed. As an exception if an exception occurs we close it ourselves */ if (!res) { PyErr_Print(); if (conn) z_stream_close(conn->stream, NULL); } else if (res == z_policy_none) { gchar buf[256]; /*LOG This message indicates that the decision layer denied the given connection. */ z_log(NULL, CORE_POLICY, 1, "Connection denied by policy; %s", z_connection_format(conn, buf, sizeof(buf))); /* close(fd); */ } Py_XDECREF(res); z_policy_thread_release(self->policy_thread); if (conn) z_connection_destroy(conn, FALSE); z_return(TRUE); } /** * z_policy_dispatch_destroy_notify: * @p this * * This function is used as the DestroyNotify callback for the * registered ZDispatchEntry. Deregisters a ZPolicyDispatch/Dispatch * instance from its policy. */ static void z_policy_dispatch_destroy_notify(gpointer p) { ZPolicyDispatch *self = (ZPolicyDispatch *) p; ZPolicy *policy; policy = z_policy_ref(self->policy); z_policy_acquire_main(policy); Py_XDECREF(self); z_policy_release_main(policy); z_policy_unref(policy); } /** * z_policy_dispatch_destroy_method: * @self this * @args unused * * Detaches a ZPolicyDispatch instance from its dispatch entry ?and from * Python? * * Returns: Py_None */ static PyObject * z_policy_dispatch_destroy_method(ZPolicyDispatch *self, PyObject *args G_GNUC_UNUSED) { if (self->dispatch) { /* our destroy_notify callback locks the interpreter explicitly, thus * we need to release it here */ Py_BEGIN_ALLOW_THREADS; z_dispatch_unregister(self->dispatch); Py_END_ALLOW_THREADS; self->dispatch = NULL; } Py_XDECREF(self->handler); self->handler = NULL; return z_policy_none_ref(); } /** * z_policy_dispatch_getattr: * @o ?Python object? * @name ?Method name? * * ?Finds a method registered to Python by its name? * * Returns: * ?The method? */ static PyObject * z_policy_dispatch_getattr(PyObject *o, char *name) { PyObject *back; z_enter(); back = Py_FindMethod(z_policy_dispatch_methods, o, name); z_leave(); return back; } /** * z_policy_dispatch_new_instance: * @o unused * @args Python arguments: session_id, protocol, addr, prio, handler, keywords * * Constructor of ZPolicyDispatch/Dispatch. Creates a new instance, and registers * a new dispatcher (self->dispatch), setting its callback to * z_policy_dispatch_accept. * * Returns: * The new instance */ static PyObject * z_policy_dispatch_new_instance(PyObject *o G_GNUC_UNUSED, PyObject *args) { ZPolicyDispatch *self = NULL; PyObject *addr; ZSockAddr *bound_addr; PyObject *handler, *keywords, *fake_args = NULL; ZDispatchBind *db; gint prio; gchar buf[MAX_SOCKADDR_STRING], *session_id; ZDispatchParams params; gint session_limit_dummy; /* session_limit is a noop */ gchar *tcp_keywords[] = { "accept_one", "backlog", "threaded", "mark_tproxy", "transparent", NULL }; gchar *udp_keywords[] = { "session_limit", "rcvbuf", "threaded", "mark_tproxy", "transparent", NULL }; /* called by python, so interpreter is locked */ if (current_policy == NULL) { PyErr_SetString(PyExc_RuntimeError, "Parsing phase has not completed yet, Listener & Receiver must be defined in the instance init() function."); return NULL; } /* res is a borrowed reference, no need to unref it */ if (!PyArg_ParseTuple(args, "sOiOO", &session_id, &addr, &prio, &handler, &keywords)) return NULL; if (!PyCallable_Check(handler)) { PyErr_SetString(PyExc_TypeError, "Handler parameter must be callable"); return NULL; } if (!z_policy_dispatch_bind_check(addr)) { PyErr_SetString(PyExc_TypeError, "addr parameter must be a DispatchBind object (DBIface or DBSockAddr)"); return NULL; } /* from this point, we must exit by goto error_exit */ db = z_policy_dispatch_bind_get_db(addr); fake_args = PyTuple_New(0); params.common.threaded = FALSE; params.common.mark_tproxy = FALSE; params.common.transparent = FALSE; switch (db->protocol) { case ZD_PROTO_TCP: params.tcp.accept_one = FALSE; params.tcp.backlog = 255; if (!PyArg_ParseTupleAndKeywords(fake_args, keywords, "|iiiii", tcp_keywords, ¶ms.tcp.accept_one, ¶ms.tcp.backlog, ¶ms.common.threaded, ¶ms.common.mark_tproxy, ¶ms.common.transparent)) { goto error_exit; } break; case ZD_PROTO_UDP: /* NOTE: params.udp.tracker is a (gchar *) valid only as long as the * z_dispatch_register calls returns, it is discarded by Python * afterwards. This is not a problem as this name is used in * z_conntrack_new, and never referenced again */ params.udp.rcvbuf = 65536; if (!PyArg_ParseTupleAndKeywords(fake_args, keywords, "|iiiii", udp_keywords, &session_limit_dummy, ¶ms.udp.rcvbuf, ¶ms.common.threaded, ¶ms.common.mark_tproxy, ¶ms.common.transparent)) { goto error_exit; } break; } self = PyObject_New(ZPolicyDispatch, &z_policy_dispatch_type); if (!self) goto error_exit; /*LOG This message indicates that a Listener on the given local address is started. */ z_log(session_id, CORE_DEBUG, 7, "Dispatcher on address; local='%s', prio='%d'", z_dispatch_bind_format(db, buf, sizeof(buf)), prio); Py_XINCREF(self); self->handler = handler; Py_XINCREF(handler); self->policy = z_policy_ref(current_policy); self->threaded = ((ZDispatchCommonParams *) ¶ms)->threaded; self->policy_thread = z_policy_thread_new(self->policy); z_policy_thread_ready(self->policy_thread); /* z_dispatch_register uses a lock also locked by the callback mechanism which keeps it locked while our callback is running, this makes a possible cross-lock deadlock: 1) This function (Python lock) -> z_dispatch_register (chain lock) 2) z_dispatch_callback (chain lock) -> our callback (Python lock) That's the reason for BEGIN_ALLOW_THREADS here. */ Py_BEGIN_ALLOW_THREADS; self->dispatch = z_dispatch_register(session_id, db, &bound_addr, prio, ¶ms, z_policy_dispatch_accept, self, z_policy_dispatch_destroy_notify); Py_END_ALLOW_THREADS; if (bound_addr) { /* In case of unspecified ports for DBSockAddr dispatch bind structures * * we update the port to the value we're actually bound to */ if (db->type == ZD_BIND_SOCKADDR) { if (z_sockaddr_inet_check(db->sa.addr)) { z_sockaddr_inet_set_port(db->sa.addr, z_sockaddr_inet_get_port(bound_addr)); } else if (z_sockaddr_inet6_check(db->sa.addr)) { z_sockaddr_inet6_set_port(db->sa.addr, z_sockaddr_inet6_get_port(bound_addr)); } } z_sockaddr_unref(bound_addr); } if (!self->dispatch) { Py_XDECREF(self); Py_XDECREF(self); PyErr_SetString(PyExc_IOError, "Error binding to interface"); self = NULL; } error_exit: Py_XDECREF(fake_args); z_dispatch_bind_unref(db); return (PyObject *) self; } /** * z_policy_dispatch_free: * @self this * * Destructor of ZPolicyDispatch/Dispatch. */ static void z_policy_dispatch_free(ZPolicyDispatch *self) { if (self->handler) { Py_XDECREF(self->handler); self->handler = NULL; } g_assert(self->dispatch == NULL); if (self->policy_thread) { Py_BEGIN_ALLOW_THREADS; /* python must be unlocked */ z_policy_thread_destroy(self->policy_thread); Py_END_ALLOW_THREADS; self->policy_thread = NULL; } if (self->policy) { z_policy_unref(self->policy); self->policy = NULL; } PyObject_Del(self); } static PyMethodDef z_policy_dispatch_methods[] = { { "destroy", (PyCFunction) z_policy_dispatch_destroy_method, 0, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; static PyTypeObject z_policy_dispatch_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "ZPolicyDispatch", .tp_basicsize = sizeof(ZPolicyDispatch), .tp_dealloc = (destructor) z_policy_dispatch_free, .tp_getattr =(getattrfunc) z_policy_dispatch_getattr, .tp_doc = "ZPolicyDispatch class for Zorp", }; /** * z_policy_dispatch_get_kzorp_result * @o unused * @args Python arguments: fd * * Queries the KZorp results for the fd passed in through the * getsockopt() interface of KZorp. * * Returns: A tuple consisting of the results: (client_zone_name, * server_zone_name, dispatcher_name, service_name) or None if the * lookup was not successful. */ static PyObject * z_policy_dispatch_get_kzorp_result(PyObject *o G_GNUC_UNUSED, PyObject *args) { gint fd; gint family; struct z_kzorp_lookup_result buf; PyObject *ret; if (!PyArg_ParseTuple(args, "ii", &family, &fd)) { return z_policy_none_ref(); } memset(&buf, 0, sizeof(buf)); if (!z_kzorp_get_lookup_result(family, fd, &buf)) { return z_policy_none_ref(); } ret = Py_BuildValue("(ssss)", &buf.czone_name, &buf.szone_name, &buf.dispatcher_name, &buf.service_name); return ret; } PyMethodDef z_policy_dispatch_funcs[] = { { "Dispatch", (PyCFunction) z_policy_dispatch_new_instance, METH_VARARGS, NULL }, { "DBSockAddr", (PyCFunction) z_policy_dispatch_bind_new_instance_sa, METH_VARARGS | METH_KEYWORDS, NULL }, { "DBIface", (PyCFunction) z_policy_dispatch_bind_new_instance_iface, METH_VARARGS | METH_KEYWORDS, NULL }, { "DBIfaceGroup", (PyCFunction) z_policy_dispatch_bind_new_instance_iface_group, METH_VARARGS | METH_KEYWORDS, NULL }, { "getKZorpResult", (PyCFunction) z_policy_dispatch_get_kzorp_result, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; /** * z_policy_dispatch_init: * * Module initialisation */ void z_policy_dispatch_module_init(void) { Py_InitModule("Zorp.Zorp", z_policy_dispatch_funcs); } zorp-3.9.5/lib/pypolicy.c000066400000000000000000001007111172670260400153110ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : kisza * Last audited version: 1.20 * Notes: * - comments * ***************************************************************************/ /* * This is an implementation of the policy interface with the python * interpreter */ #include #include #include /* for python initialization functions */ #include #include #include #include #include #include #include #include #include /* for capability management */ #include ZPolicy *current_policy; /* Notification thread function and indicating policy termination */ extern gpointer z_notification_thread_main_func(gpointer data); extern void _notify_event_queue_terminate_request(ZPolicy *policy); const gchar * z_verdict_str(ZVerdict verdict) { static const gchar *verdict_str[] = { [ZV_UNSPEC] = "Unspecified", [ZV_ACCEPT] = "Accept data", [ZV_DENY] = "Deny data", [ZV_REJECT] = "Reject data", [ZV_ABORT] = "Abort connection", [ZV_DROP] = "Drop data", [ZV_POLICY] = "Call policy", [ZV_ERROR] = "Error while processing", }; if ((guint) verdict <= 7) return verdict_str[(guint) verdict]; return "Unknown"; } /* helper functions used by proxies */ gboolean z_policy_tuple_get_verdict(ZPolicyObj *tuple, guint *verdict) { ZPolicyObj *tmp; z_enter(); if (!z_policy_seq_check(tuple)) { if (z_policy_var_parse(tuple, "i", verdict)) z_return(TRUE); /* not a sequence nor an int */ z_return(FALSE); } tmp = z_policy_seq_getitem(tuple, 0); if (!tmp || !z_policy_var_parse(tmp, "i", verdict)) { /* policy syntax error */ z_policy_var_unref(tmp); z_return(FALSE); } z_policy_var_unref(tmp); z_return(TRUE); } /** * z_policy_convert_strv_to_list: * @strv: String array to 'listify' * * Converts an array of C strings to Python list of strings. * * Returns: * The resulting list */ PyObject * z_policy_convert_strv_to_list(gchar const **strv) { PyObject *list; gint i; list = PyList_New(0); for (i = 0; strv[i]; i++) { PyList_Append(list, PyString_FromString(strv[i])); } return list; } /** * z_policy_getattr_expr: * @container: Base Python object whose attribute is to be set * @name: Name of the attribute (see notes below!) * * Gets an attribute of a Python object, or one of its embedded objects, and * so on. * If @name is in the form of 'Emb1.Emb2. ... .EmbN.Attribute', then * container.Emb1.Emb2. ... .EmbN.Attribute will be asked for. * Certainly, if @name is only 'Attribute', then container.Attribute will be * returned. * * Returns: * The attribute value on success, otherwise NULL */ PyObject * z_policy_getattr_expr(PyObject *container, const char *name) { PyObject *p, *new_p; gchar **attr; gint i; attr = g_strsplit(name, ".", 0); p = container; Py_XINCREF(p); for (i = 0; attr[i] && p; i++) { new_p = PyObject_GetAttrString(p, attr[i]); Py_XDECREF(p); p = new_p; } g_strfreev(attr); return p; } /** * z_policy_setattr_expr: * @container: Base Python object whose attribute is to be set * @name: Name of the attribute (see notes below!) * @new_value: New attribute value * * Sets an attribute of a Python object, or one of its embedded objects, and * so on. * If @name is in the form of 'Emb1.Emb2. ... .EmbN.Attribute', then * container.Emb1.Emb2. ... .EmbN.Attribute will be set to @new_value * Certainly, if @name is only 'Attribute', then container.Attribute will be set. * * Returns: * 1 on success, 0 otherwise */ gint z_policy_setattr_expr(PyObject *container, const char *name, PyObject *new_value) { PyObject *p, *new_p; gchar **attr; gint i; gint res = 0; attr = g_strsplit(name, ".", 0); p = container; Py_XINCREF(p); for (i = 0; attr[i] && attr[i+1] && p; i++) { new_p = PyObject_GetAttrString(p, attr[i]); Py_XDECREF(p); p = new_p; } if (p && attr[i]) { PyObject_SetAttrString(p, attr[i], new_value); res = 1; } Py_XDECREF(p); g_strfreev(attr); return res; } /** * z_policy_setattr: * @handler: Python object whose attribute is to be set * @name: Attribute name * @value: New attribute value * * Same as z_policy_setattr_expr, but in case of failure, it clears the * error by PyErr_Clear() * * Returns: * 1 on success, 0 otherwise */ gint z_policy_setattr(PyObject *handler, char *name, PyObject *value) { if (z_policy_setattr_expr(handler, name, value) == 0) { z_policy_error_clear(); return 0; } return 1; } /** * z_policy_getattr: * @handler: Python object whose attribute is requested * @name: Attribute name * * Same as z_policy_getattr_expr, but in case of failure, it clears the * error by PyErr_Clear() * * Returns: * The attribute value on success, otherwise NULL */ PyObject * z_policy_getattr(PyObject *handler, char *name) { PyObject *res; res = z_policy_getattr_expr(handler, name); if (!res) z_policy_error_clear(); return res; } /** * z_session_getattr: * @handler: Python object whose session attribute is requested * @name: Attribute name * * Gets @handler.session.@name * FIXME: some words about its meaning * * Returns: * The attribute value on success, otherwise NULL */ PyObject * z_session_getattr(PyObject *handler, gchar *name) { gchar buf[64]; PyObject *res; g_snprintf(buf, sizeof(buf), "session.%s", name); res = z_policy_getattr_expr(handler, buf); if (!res) z_policy_error_clear(); return res; } /** * z_global_getattr: * @name: Global attribute name * * Gets a global attribute * FIXME: are these the ones in 'policy.py'? * * Returns: * The attribute value on success, otherwise NULL */ PyObject * z_global_getattr(const gchar *name) { PyObject *main_module, *res; main_module = PyImport_AddModule("__main__"); res = z_policy_getattr_expr(main_module, name); if (!res) z_policy_error_clear(); return res; } /** * z_policy_var_parse_str: * @val: PyObject to parse * @result: result is strdup-ed here * * This function tries to parse @val as a string and strdups the value of * the string into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_str(z_global_getattr("config.module.string_variable"), &c_variable_string); **/ gboolean z_policy_var_parse_str(PyObject *val, gchar **result) { gchar *strvalue = NULL; gboolean res = FALSE; if (val) { if (z_policy_var_parse(val, "s", &strvalue)) { *result = g_strdup(strvalue); res = TRUE; } Py_XDECREF(val); } return res; } /** * z_policy_var_parse_boolean: * @val: PyObject to parse * @result: result * * This function tries to parse @val as a boolean and puts the parsed value * into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_boolean(z_global_getattr("config.module.boolean_variable"), &c_variable_boolean); **/ gboolean z_policy_var_parse_boolean(PyObject *val, gboolean *result) { gboolean success; gint intval = 0; success = z_policy_var_parse_int(val, &intval); *result = !!intval; return success; } /** * z_policy_var_parse_int: * @val: PyObject to parse * @result: result * * This function tries to parse @val as an integer and puts the parsed value * into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_int(z_global_getattr("config.module.integer_variable"), &vc_variable_integer); **/ gboolean z_policy_var_parse_int(PyObject *val, gint *result) { gboolean res = TRUE; if (val) { if (!z_policy_var_parse(val, "i", result)) { res = FALSE; } Py_XDECREF(val); } return res; } /** * z_policy_var_parse_uint: * @val: PyObject to parse * @result: result * * This function tries to parse @val as an unsigned integer and puts the parsed value * into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_uint(z_global_getattr("config.module.integer_variable"), &vc_variable_integer); **/ gboolean z_policy_var_parse_uint(PyObject *val, guint *result) { gboolean res = TRUE; if (val) { if (!z_policy_var_parse(val, "I", result)) { res = FALSE; } Py_XDECREF(val); } return res; } /** * z_policy_var_parse_size: * @val: PyObject to parse * @result: result * * This function tries to parse @val as a gsize and puts the parsed value * into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_size(z_global_getattr("config.module.gsize_variable"), &c_variable_gsize); **/ gboolean z_policy_var_parse_size(PyObject *val, gsize *result) { gboolean res = TRUE; if (val) { switch (sizeof(gsize)) { case sizeof(gchar): res = z_policy_var_parse(val, "b", result); break; case sizeof(gshort): res = z_policy_var_parse(val, "h", result); break; case sizeof(gint): res = z_policy_var_parse(val, "i", result); break; case sizeof(gint64): res = z_policy_var_parse(val, "L", result); break; default: g_assert(0); /* NOTE: crash out instead of letting the error escalate */ break; } Py_XDECREF(val); } return res; } /** * z_policy_var_parse_int64: * @val: PyObject to parse * @result: result * * This function tries to parse @val as a (signed) 64-bit wide integer and puts the parsed value * into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_int64(z_global_getattr("config.module.int64_variable"), &c_variable_gint64); **/ gboolean z_policy_var_parse_int64(PyObject *val, gint64 *result) { gboolean res = TRUE; if (val) { if (!z_policy_var_parse(val, "L", result)) { res = FALSE; } Py_XDECREF(val); } return res; } /** * z_policy_var_parse_uint64: * @val: PyObject to parse * @result: result * * This function tries to parse @val as an unsigned 64-bit wide integer and puts the parsed value * into @result. It always consumes @val, thus the caller loses * its reference. This function can be used to quickly parse Python values * this way: * * z_policy_var_parse_uint64(z_global_getattr("config.module.int64_variable"), &c_variable_gint64); **/ gboolean z_policy_var_parse_uint64(PyObject *val, guint64 *result) { gboolean res = TRUE; if (val) { if (!z_policy_var_parse(val, "K", result)) { res = FALSE; } Py_XDECREF(val); } return res; } /** * z_policy_call_object: * @func: Python method to call * @args: Arguments to pass to @func * @session_id: Session ID for logging * * Calls @func with @args, and if an error happened, sends log messages * (containing @session_id) to the log. * * Returns: * The return value of @func */ PyObject * z_policy_call_object(PyObject *func, PyObject *args, gchar *session_id) { PyObject *res; PyErr_Clear(); res = PyObject_CallObject(func, args); Py_XDECREF(args); if (!res) { PyObject *m = PyImport_AddModule("sys"); PyObject *exc, *value, *tb, *what_str; PyErr_Fetch(&exc, &value, &tb); what_str = PyString_FromString("what"); if (PyObject_HasAttr(value, what_str)) { PyObject *what = PyObject_GetAttr(value, what_str); PyObject *detail_str = PyString_FromString("detail"); if (PyObject_HasAttr(value, detail_str)) { PyObject *detail = PyObject_GetAttr(value, detail_str); z_log(session_id, CORE_ERROR, 3, "%s; reason='%s'", PyString_AsString(what), PyString_AsString(detail)); Py_XDECREF(detail); } else { z_log(session_id, CORE_ERROR, 3, "%s;", PyString_AsString(what)); } Py_XDECREF(what); Py_XDECREF(detail_str); Py_XDECREF(exc); Py_XDECREF(value); Py_XDECREF(tb); } else { PyErr_Restore(exc, value, tb); PyErr_Print(); } Py_XDECREF(what_str); PyObject_SetAttrString(m, "last_traceback", Py_None); } return res; } /** * z_policy_call: * @handler: Python object whose method shall be called * @name: Method name * @args: Arguments to pass to the method * @called: Flag to store into whether the call succeeded or not (may be NULL) * @session_id: Session ID for logging * * If the requested method exists and is callable, calls it. * If @called is not NULL, it will be set if the call succeeded, cleared if not. * * Returns: * The return value of the call */ PyObject * z_policy_call(PyObject *handler, char *name, PyObject *args, gboolean *called, gchar *session_id) { PyObject *attr; PyObject *res; z_enter(); g_assert(PyThreadState_GET()); attr = PyObject_GetAttrString(handler, name); if (!attr || !PyCallable_Check(attr)) { if (attr) { Py_XDECREF(attr); PyErr_Format(PyExc_TypeError, "Event must be callable: %s", name); PyErr_Print(); /* produce a backtrace, and handle it immediately */ } PyErr_Clear(); Py_XDECREF(args); res = NULL; z_trace(NULL, "Cannot find function; name='%s'", name); if (called) *called = FALSE; } else { if (called) *called = TRUE; res = z_policy_call_object(attr, args, session_id); z_trace(NULL, "Function called; name='%s'", name); Py_XDECREF(attr); } z_return(res); } /** * z_policy_event: * @handler: Python object whom to send the event * @name: Method to be called * @args: Args to pass to @name * @session_id: Session ID * * Sends an event to an object by calling one of its methods. The methods * 'preProcessEvent' and 'postProcessEvent' are called before and after the * event handler method. * * FIXME: I found no occurences of 'ProcessEvent' but in this function, so I * can't track the purpose of this functionality. * * Returns: * ZV_UNSPEC, ZV_ABORT, ??? */ gint z_policy_event(PyObject *handler, char *name, PyObject *args, gchar *session_id) { PyObject *res; unsigned long c_res; gboolean called; Py_XINCREF(args); res = z_policy_call(handler, "preProcessEvent", args, &called, session_id); if (res) { if (PyInt_Check(res)) { c_res = PyInt_AsLong(res); Py_XDECREF(res); if (c_res != ZV_UNSPEC) { Py_XDECREF(args); return c_res; } } else { PyErr_Format(PyExc_TypeError, "preProcessEvent() handlers should return an int."); PyErr_Print(); /* produce a backtrace, and handle it immediately */ Py_XDECREF(res); } } else if (called) return ZV_ABORT; Py_XINCREF(args); res = z_policy_call(handler, name, args, &called, session_id); if (res) { if (PyInt_Check(res)) { c_res = PyInt_AsLong(res); Py_XDECREF(res); if (c_res != ZV_UNSPEC) { Py_XDECREF(args); return c_res; } } else { PyErr_Format(PyExc_TypeError, "Event handlers should return an int: %s", name); PyErr_Print(); /* produce a backtrace, and handle it immediately */ Py_XDECREF(res); } } else if (called) return ZV_ABORT; res = z_policy_call(handler, "postProcessEvent", args, &called, session_id); if (res) { if (PyInt_Check(res)) { c_res = PyInt_AsLong(res); Py_XDECREF(res); return c_res; } else { PyErr_Format(PyExc_TypeError, "postProcessEvent() handlers should return an int."); PyErr_Print(); /* produce a backtrace, and handle it immediately */ Py_XDECREF(res); } } else if (called) return ZV_ABORT; return ZV_UNSPEC; } /* loadable policies */ GStaticMutex policy_ref_lock = G_STATIC_MUTEX_INIT; struct _ZPolicyThread { ZPolicy *policy; PyThreadState *thread; /* thread startup synchronization */ gboolean startable:1, used:1; GMutex *startable_lock; GCond *startable_signal; }; GStaticPrivate policy_thread = G_STATIC_PRIVATE_INIT; static gboolean z_policy_purge(ZPolicy *self); #include /** * z_policy_thread_ready: * @self: this * * Marks the thread ready for starting by setting its 'startable' flag. */ void z_policy_thread_ready(ZPolicyThread *self) { g_mutex_lock(self->startable_lock); self->startable = TRUE; g_cond_signal(self->startable_signal); g_mutex_unlock(self->startable_lock); } /** * z_policy_thread_wait: * @self: this * * Waits until the thread gets ready for starting */ static void z_policy_thread_wait(ZPolicyThread *self) { g_mutex_lock(self->startable_lock); while (!self->startable) { g_cond_wait(self->startable_signal, self->startable_lock); } g_mutex_unlock(self->startable_lock); } /** * z_policy_thread_acquire: * @self: this * * Acquires and stores reference information about the current thread. * self->thread is the PyThreadState (context on the Python-side), * policy_thread is the thread-private ZPolicyThread* (context on the C-side). */ void z_policy_thread_acquire(ZPolicyThread *self) { z_policy_thread_wait(self); g_static_private_set(&policy_thread, self, NULL); PyEval_AcquireThread(self->thread); /* NOTE: this is currently a warning, but it'd probably make sense to * actually exclude parallel execution in the same thread by using a mutex * in ZPolicyThread. However as this is a risky change at 3.1.x, x >= 14 I * only added a warning here. */ if (self->used) { #if 0 z_log(NULL, CORE_ERROR, 0, "Internal error, ZPolicyThread reused, dumping core & continuing;"); z_coredump_create(); #endif } self->used = TRUE; } /** * z_policy_thread_release: * @self: this * * Releases reference information acquired by z_policy_thread_acquire. */ void z_policy_thread_release(ZPolicyThread *self) { self->used = FALSE; PyEval_ReleaseThread(self->thread); g_static_private_set(&policy_thread, NULL, NULL); } /** * z_policy_thread_self: * * Get the ZPolicyThread context of the current thread. * Uses the thread-private variable 'policy_thread', set by * 'z_policy_thread_acquire'. * * Returns: * The current context. */ ZPolicyThread * z_policy_thread_self(void) { return g_static_private_get(&policy_thread); } /** * z_policy_thread_get_policy: * @self: this * * Get method for the 'policy' attribute * * Returns: * The policy */ ZPolicy * z_policy_thread_get_policy(ZPolicyThread *self) { return self->policy; } /** * z_policy_thread_new: * @policy: The policy to create a thread for * * Constructor of ZPolicyThread. If the policy already has a main thread, * only acquire a new ThreadState instance (start a new thread in that * interpreter), else start a new interpreter. * * Returns: * The new instance */ ZPolicyThread * z_policy_thread_new(ZPolicy *policy) { ZPolicyThread *self = g_new0(ZPolicyThread, 1); /* NOTE: requires the interpreter lock to be held */ self->startable = FALSE; self->startable_lock = g_mutex_new(); self->startable_signal = g_cond_new(); self->policy = z_policy_ref(policy); if (policy->main_thread) { self->thread = PyThreadState_New(self->policy->main_thread->thread->interp); } else { /* initialize a new interpreter instance */ self->thread = Py_NewInterpreter(); PyThreadState_Swap(NULL); } return self; } /** * z_policy_thread_destroy: * @self: this * * Destructor of ZPolicyThread. * The embedded Python thread context (self->thread) will be cleared and * deleted also, and if this thread was the last one running in the * interpreter instance, that will be stopped, too. */ void z_policy_thread_destroy(ZPolicyThread *self) { /* acquires the interpreter lock */ if (self->policy->main_thread != self) { /* we are one of the secondary threads */ z_python_lock(); PyThreadState_Swap(self->thread); PyThreadState_Clear(self->thread); PyThreadState_Swap(NULL); PyThreadState_Delete(self->thread); z_python_unlock(); z_policy_unref(self->policy); } else { /* we must be freed at last, when the policy is being destroyed */ g_assert(self->policy->ref_cnt == 1); /* we are the main thread, destroy the interpreter */ z_policy_purge(self->policy); PyEval_AcquireThread(self->thread); Py_EndInterpreter(self->thread); z_python_unlock(); } g_mutex_free(self->startable_lock); g_cond_free(self->startable_signal); g_free(self); } /** * z_policy_free: * @self: this * * Destroys a ZPolicy by freeing its filename and destroying its threads. */ static void z_policy_free(ZPolicy *self) { /* FIXME: we should acquire a thread state to be able to free ourselves */ g_free(self->policy_filename); /* NOTE: This must be the last because that destroyer assume that he is the last men standing. */ z_policy_thread_destroy(self->main_thread); g_free(self); } /** * z_policy_ref: * @self: this * * Increments the reference counter of a ZPolicy. * Each policy reference counter is protected by the interpreter lock * * Returns: * this */ ZPolicy * z_policy_ref(ZPolicy *self) { g_static_mutex_lock(&policy_ref_lock); g_assert(self->ref_cnt > 0); self->ref_cnt++; g_static_mutex_unlock(&policy_ref_lock); return self; } /** * z_policy_unref: * @self: this * * Decrements the reference counter of a ZProxy, freeing the instance if * the counter drops to zero. */ void z_policy_unref(ZPolicy *self) { g_static_mutex_lock(&policy_ref_lock); g_assert(self->ref_cnt > 0); --self->ref_cnt; /* NOTE: This reference counter counts two different reference types: * * the number of references to this ZPolicy instance by the rest of Zorp * * the number of internal references between ZPolicy and the * ZPolicyThread instances started in z_policy_new(): main thread * and notification thread * * The main and notification threads form circular references, their * destruction must be triggered somehow. Whenever the number of * references go low enough we trigger destruction, which will eventually * cause the refcounter to go to 0. * * The trigger to terminate the notification thread is also sent from * here, when the refcount goes to 3. * */ /* main thread */ #define ZPOLICY_BASE_REFCOUNT 1 if (self->ref_cnt == ZPOLICY_BASE_REFCOUNT) { /* ok, only the notification thread & main thread remains, start destructing them now */ g_static_mutex_unlock(&policy_ref_lock); z_policy_free(self); } else { g_static_mutex_unlock(&policy_ref_lock); } } /** * z_policy_boot: * @self: this * * 'Boots' a policy: * - loads policy.boot (importing necessary modules) * - initialises the modules * * FIXME?: the only check is for the read access on policy.boot * there is no check whether it could be interpreted correctly, * neither for the modules' initialisations * * Returns: * TRUE if policy.boot exists */ gboolean z_policy_boot(ZPolicy *self) { FILE *bootstrap; if ((bootstrap = fopen(ZORP_POLICY_BOOT_FILE, "r")) == NULL) { /*LOG This message indicates that the policy.boot file couldn't be opened for reading. */ z_log(NULL, CORE_ERROR, 0, "Error opening policy.boot file; file='%s'", ZORP_POLICY_BOOT_FILE); return FALSE; } z_policy_thread_acquire(self->main_thread); PyRun_SimpleFile(bootstrap, ZORP_POLICY_BOOT_FILE); fclose(bootstrap); /* PyImport_AddModule("Zorp.Zorp"); */ z_py_zorp_core_init(); z_policy_struct_module_init(); z_policy_dispatch_module_init(); z_policy_attach_module_init(); z_policy_stream_module_init(); z_policy_proxy_module_init(); z_policy_sockaddr_module_init(); z_policy_proxy_group_module_init(); z_policy_thread_release(self->main_thread); return TRUE; } /** * z_policy_load: * @self: this * * Load and run a policy file. If the file doesn't exist or a parse error * happens, produces an error message in the log. * * Returns: * TRUE on success */ gboolean z_policy_load(ZPolicy *self) { FILE *script; int res = -1; script = fopen(self->policy_filename, "r"); if (script) { z_policy_thread_acquire(self->main_thread); res = PyRun_SimpleFile(script, self->policy_filename); fclose(script); z_policy_thread_release(self->main_thread); } else { /*LOG This message indicates that Zorp was unable to open the policy file. Check the permissions of your policy file. */ z_log(NULL, CORE_ERROR, 0, "Error opening policy file; filename='%s'", self->policy_filename); } if (res == -1) { /*LOG This message indicates that Zorp was unable to parse the policy file. Check the logs for further information on where the error occurred in the policy. */ z_log(NULL, CORE_ERROR, 0, "Error parsing policy file; filename='%s'", self->policy_filename); /* let the error message out */ } return res != -1; } /** * z_policy_init: * @self: this * @instance_name: array of instance name and aliases * @virtual_instance_name: virtual instance name of this process * * Initialises the current policy by calling ?Zorp.init?. * The function interface (not the implementation) here should be * independent of python. * * Returns: * TRUE on success */ gboolean z_policy_init(ZPolicy *self, gchar const **instance_name, gchar const *virtual_instance_name, gboolean is_master) { PyObject *main_module, *init_func, *res; gboolean success = FALSE; cap_t saved_caps; z_policy_thread_acquire(self->main_thread); main_module = PyImport_AddModule("__main__"); init_func = PyObject_GetAttrString(main_module, "init"); saved_caps = cap_save(); cap_enable(CAP_NET_ADMIN); res = PyObject_CallFunction(init_func, "(Osi)", z_policy_convert_strv_to_list(instance_name), virtual_instance_name, is_master); cap_restore(saved_caps); Py_XDECREF(init_func); if (res && z_policy_var_parse(res, "i", &success)) { /* init successful */ } else if (!res) { PyErr_Print(); } Py_XDECREF(res); z_policy_thread_release(self->main_thread); return success; } /** * z_policy_deinit: * @self: this * @instance_name: array of instance name and aliases * @virtual_instance_name: virtual instance name of this process * * Deinitialises the current policy by calling ?Zorp.deinit?. * * Returns: * TRUE on success */ gboolean z_policy_deinit(ZPolicy *self, gchar const **instance_name, gchar const *virtual_instance_name) { PyObject *main_module, *deinit_func, *res; z_policy_thread_acquire(self->main_thread); main_module = PyImport_AddModule("__main__"); deinit_func = PyObject_GetAttrString(main_module, "deinit"); res = PyObject_CallFunction(deinit_func, "(Os)", z_policy_convert_strv_to_list(instance_name), virtual_instance_name); Py_XDECREF(deinit_func); if (!res) { PyErr_Print(); } Py_XDECREF(res); z_policy_thread_release(self->main_thread); return res != NULL; } /** * z_policy_purge: * @self: this * * Purge the current thread context by calling ?Zorp.purge? * FIXME: z_policy_thread_acquire(self->main_thread) -?-> z_policy_acquire_main(self) * * Returns: * TRUE on success */ static gboolean z_policy_purge(ZPolicy *self) { PyObject *main_module, *purge_func, *res; z_policy_thread_acquire(self->main_thread); main_module = PyImport_AddModule("__main__"); purge_func = PyObject_GetAttrString(main_module, "purge"); res = PyObject_CallFunction(purge_func, "()"); Py_XDECREF(purge_func); if (!res) { PyErr_Print(); } Py_XDECREF(res); z_policy_thread_release(self->main_thread); return res != NULL; } /** * z_policy_cleanup: * @self: this * @instance_name: array of instance name and aliases * @virtual_instance_name: virtual instance name of this process * * Cleans up the current policy by calling ?Zorp.cleanup?. * Currently used by KZorp to flush kernel data structures * when Zorp is exiting. * * Returns: * TRUE on success */ gboolean z_policy_cleanup(ZPolicy *self, gchar const **instance_name, gchar const *virtual_instance_name, gboolean is_master) { PyObject *main_module, *cleanup_func, *res; cap_t saved_caps; z_policy_thread_acquire(self->main_thread); main_module = PyImport_AddModule("__main__"); cleanup_func = PyObject_GetAttrString(main_module, "cleanup"); saved_caps = cap_save(); cap_enable(CAP_NET_ADMIN); res = PyObject_CallFunction(cleanup_func, "(Osi)", z_policy_convert_strv_to_list(instance_name), virtual_instance_name, is_master); cap_restore(saved_caps); Py_XDECREF(cleanup_func); if (!res) { PyErr_Print(); } Py_XDECREF(res); z_policy_thread_release(self->main_thread); return res != NULL; } /** * z_policy_acquire_main: * @self: this * * Switch to the context of the main thread */ void z_policy_acquire_main(ZPolicy *self) { z_policy_thread_acquire(self->main_thread); } /** * z_policy_release_main: * @self: this * * Leave the context of the main thread. * Note that this doesn't imply 'returning to the previous context', for that * you must call z_policy_acquire() explicitely. */ void z_policy_release_main(ZPolicy *self) { z_policy_thread_release(self->main_thread); } /** * z_policy_new: * @filename: Name of the policy file * * Constructor of ZPolicy, creates a new instance and starts its main thread * and notification thread. The policy's refcount is 3: one per thread * plus one for the caller. * * Returns: * The new instance */ ZPolicy * z_policy_new(const gchar *filename) { ZPolicy *self = g_new0(ZPolicy, 1); self->ref_cnt = 1; self->policy_filename = g_strdup(filename); z_python_lock(); self->main_thread = z_policy_thread_new(self); z_python_unlock(); z_policy_thread_ready(self->main_thread); /* the main thread and the notification thread always references us, * and we should be deleted when that reference is dropped */ return self; } void z_policy_raise_exception_obj(PyObject *exc, gchar *desc) { PyErr_SetString(exc, desc); } /** * z_policy_raise_exception: * @exception_name: Name of the exception * @desc: Description * * Generate a Python exception with the given name and descriptions */ void z_policy_raise_exception(gchar *exception_name, gchar *desc) { PyObject *main_module, *license_exc; main_module = PyImport_AddModule("__main__"); license_exc = PyObject_GetAttrString(main_module, exception_name); PyErr_SetString(license_exc, desc); Py_XDECREF(license_exc); } zorp-3.9.5/lib/pyproxy.c000066400000000000000000000260131172670260400151750ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : kisza * Last audited version: 1.15 * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include /* ZorpProxy is the Python interface to ZProxy. */ struct _ZPolicyProxy { PyObject_HEAD ZProxy *proxy; ZProxy *parent_proxy; ZPolicyObj *client_stream; ZPolicyObj *session_id; ZPolicyObj *module_name; ZPolicyObj *proxy_name; }; /** * z_policy_proxy_get_proxy: * @obj this * * Get the embedded proxy. * * Returns: * The embedded proxy */ ZProxy * z_policy_proxy_get_proxy(PyObject *obj) { return ((ZPolicyProxy *) obj)->proxy; } /** * z_policy_proxy_bind: * @self this * @args Python args: module_name, session_id, client, parent * * Constructor of ZPolicyProxy. Class is an abstract one, so the instance * already exists, only initialisation has to be done. * * Searches the registry for module_name, and creates its (C-side) self->proxy * using the returned constructor. For details see documentation about the * instantiation and invocation of custom proxies. * * Returns: * NULL on error, PyNone on success. */ gboolean z_policy_proxy_bind_implementation(PyObject *s) { ZPolicyProxy *self = (ZPolicyProxy *) s; ZProxyParams params; gpointer proxy_create; int proxy_type = ZR_NONE; gchar *module_name; gchar *proxy_name; z_enter(); if (self->proxy) z_return(TRUE); module_name = PyString_AsString(self->module_name); proxy_name = PyString_AsString(self->proxy_name); proxy_create = z_registry_get(proxy_name, &proxy_type); if (!proxy_create) { if (!z_load_module(module_name)) { /*LOG This message indicates that Zorp was unable to find the required proxy module. Check your installation, or contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 1, "Cannot find proxy module; module='%s', proxy='%s, type='%d'", module_name, proxy_name, proxy_type); z_leave(); return FALSE; } proxy_create = z_registry_get(proxy_name, &proxy_type); } if (!proxy_create || (proxy_type != ZR_PROXY && proxy_type != ZR_PYPROXY)) { /*LOG This message indicates that Zorp was unable to find the required proxy module. Check your installation, or contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 1, "Cannot find proxy module; module='%s', proxy='%s, type='%d'", module_name, proxy_name, proxy_type); z_leave(); return FALSE; } params.session_id = PyString_AsString(self->session_id); params.pyclient = self->client_stream; params.client = z_policy_stream_get_stream(self->client_stream); params.handler = (ZPolicyObj *) self; params.parent = self->parent_proxy; /* params.client is referenced through self->client_stream, we need to * unref it here as the proxy constructor will take its own reference */ z_stream_unref(params.client); Py_BEGIN_ALLOW_THREADS; self->proxy = (*(ZProxyCreateFunc) proxy_create)(¶ms); Py_END_ALLOW_THREADS; z_leave(); return TRUE; } /** * z_policy_proxy_getattr: * @self this * @name Attribute name * * Get an attribute of the proxy. Actually there is nothing to get from the * abstract ZPolicyProxy, rather from its descendants, so if the embedded proxy * is initialised, its vars (or session_vars->vars) is searched for the name. * * Returns: * The attribute value */ static PyObject * z_policy_proxy_getattr(ZPolicyProxy *self, PyObject *name_obj) { PyObject *v = NULL; g_assert(PyString_Check(name_obj)); /* NOTE: we don't support fetching proxy attributes as long as the proxy * is not initialized */ if (self->proxy && self->proxy->dict && z_proxy_get_state(self->proxy) >= ZPS_CONFIG) { const gchar *name = PyString_AS_STRING(name_obj); if (strcmp(name, "proxy_started") == 0) { return PyInt_FromLong(1); } v = z_policy_dict_get_value(self->proxy->dict, z_proxy_get_state(self->proxy) == ZPS_CONFIG, name); if (v) { if (z_log_enabled(CORE_DEBUG, 6)) { PyObject *repr = PyObject_Repr(v); /*LOG This message reports that the given proxy-exported attribute was fetched, and it contained the also given value. */ z_log(self->proxy->session_id, CORE_DEBUG, 6, "Attribute fetched; attribute='%s', value='%s'", name, PyString_AsString(repr)); Py_XDECREF(repr); } return v; } } return PyObject_GenericGetAttr((PyObject *) self, name_obj); } /** * z_policy_proxy_setattr: * @self this * @name Attribute name * @value New attribute value * * Set an attribute of the proxy. The recognised attribute names are the same * as for _getattr, the type of @value is the same as _getattr would return * for @name. * * Returns: * The attribute value */ static gint z_policy_proxy_setattr(ZPolicyProxy *self, PyObject *name_obj, PyObject *value) { g_assert(PyString_Check(name_obj)); if (self->proxy && self->proxy->dict && z_proxy_get_state(self->proxy) >= ZPS_CONFIG) { const gchar *name = PyString_AS_STRING(name_obj); if (z_policy_dict_set_value(self->proxy->dict, z_proxy_get_state(self->proxy) == ZPS_CONFIG, name, value) == 0) { if (z_log_enabled(CORE_DEBUG, 6)) { PyObject *repr = PyObject_Repr(value); /*LOG This message reports that the given proxy-exported attribute was changed to the given value. */ z_log(self->proxy->session_id, CORE_DEBUG, 6, "Attribute changed; attribute='%s', newvalue='%s'", name, PyString_AsString(repr)); Py_XDECREF(repr); } return 0; } else { if (PyErr_Occurred()) { PyErr_Print(); return 1; } } } if (PyErr_Occurred()) PyErr_Print(); return PyObject_GenericSetAttr((PyObject *) self, name_obj, value); } /** * z_policy_proxy_init_instance: * @self this * @args Python args: module_name, session_id, client, parent * * Constructor of ZPolicyProxy. Class is an abstract one, so the instance * already exists, only initialisation has to be done. * * Searches the registry for module_name, and creates its (C-side) self->proxy * using the returned constructor. For details see documentation about the * instantiation and invocation of custom proxies. * * Returns: * NULL on error, PyNone on success. */ static int z_policy_proxy_init_instance(ZPolicyProxy *self, PyObject *args) { PyObject *proxy_name, *module_name, *client, *parent, *session_id; ZProxy *parent_proxy = NULL; z_enter(); if (!PyArg_ParseTuple(args, "SSSOO", &proxy_name, &module_name, &session_id, &client, &parent)) { z_log(NULL, CORE_ERROR, 2, "Invalid parameters;"); z_leave(); return -1; } if (!z_policy_stream_check(client)) { PyErr_SetString(PyExc_TypeError, "client must be a ZPolicyStream"); z_leave(); return -1; } if (parent != z_policy_none) { parent_proxy = ((ZPolicyProxy *) parent)->proxy; } z_policy_var_ref(session_id); z_policy_var_ref(client); z_policy_var_ref(module_name); self->proxy_name = proxy_name; self->module_name = module_name; self->session_id = session_id; self->client_stream = client; self->parent_proxy = z_proxy_ref(parent_proxy); z_leave(); return 0; } /** * z_policy_proxy_free: * @self this * * Destructor of ZPolicyProxy */ static void z_policy_proxy_free(ZPolicyProxy *self) { z_proxy_unref(self->proxy); z_proxy_unref(self->parent_proxy); z_policy_var_unref(self->client_stream); z_policy_var_unref(self->session_id); z_policy_var_unref(self->module_name); self->ob_type->tp_free((PyObject *) self); } static PyMethodDef z_policy_proxy_methods[] = { { NULL, NULL, 0, NULL } }; PyTypeObject z_policy_proxy_type = { PyObject_HEAD_INIT(&PyType_Type) .ob_size = 0, .tp_name = "ZPolicyProxy", .tp_basicsize = sizeof(ZPolicyProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)z_policy_proxy_free, .tp_print = NULL, .tp_getattr = NULL, .tp_setattr = NULL, .tp_compare = NULL, .tp_repr = NULL, .tp_as_number = NULL, .tp_as_sequence = NULL, .tp_as_mapping = NULL, .tp_hash = NULL, .tp_call = NULL, .tp_str = NULL, .tp_getattro = (getattrofunc) z_policy_proxy_getattr, .tp_setattro = (setattrofunc) z_policy_proxy_setattr, .tp_as_buffer = NULL, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = "ZPolicyProxy class", .tp_traverse = NULL, .tp_clear = NULL, .tp_richcompare = NULL, .tp_weaklistoffset = 0, .tp_iter = NULL, .tp_iternext = NULL, .tp_methods = z_policy_proxy_methods, .tp_members = NULL, .tp_getset = NULL, .tp_base = NULL, .tp_dict = NULL, .tp_descr_get = NULL, .tp_descr_set = NULL, .tp_dictoffset = 0, .tp_init = (initproc) z_policy_proxy_init_instance, .tp_alloc = NULL, .tp_new = PyType_GenericNew, .tp_free = NULL, .tp_is_gc = NULL, .tp_bases = NULL, .tp_mro = NULL, .tp_cache = NULL, .tp_subclasses = NULL, .tp_weaklist = NULL, .tp_del = NULL, }; /* to avoid type-punned pointer warning -- not static to avoid auto-inlining */ void wrap_Py_INCREF(void * p) { Py_INCREF(p); } /** * z_policy_proxy_module_init: * * Module initialisation. * FIXME: some words about GetDict... */ void z_policy_proxy_module_init(void) { PyObject *m; void *o; if (PyType_Ready(&z_policy_proxy_type) < 0) g_assert_not_reached(); m = PyImport_AddModule("Zorp.Zorp"); wrap_Py_INCREF(&z_policy_proxy_type); o = &z_policy_proxy_type; PyModule_AddObject(m, "BuiltinProxy", (PyObject *) o); } zorp-3.9.5/lib/pyproxygroup.c000066400000000000000000000066441172670260400162620ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : bazsi * Auditor : kisza * Last audited version: 1.4 * Notes: * ***************************************************************************/ #include #include /** * z_policy_proxy_group_start: * @self this * @args Python params (proxy_class, session) * * Returns: */ static ZPolicyObj * z_policy_proxy_group_start(gpointer user_data, ZPolicyObj *args, ZPolicyObj *kw G_GNUC_UNUSED) { ZProxyGroup *proxy_group = (ZProxyGroup *) user_data; ZPolicyObj *proxy_instance; if (!z_policy_var_parse(args, "(O)", &proxy_instance)) return NULL; if (!z_policy_proxy_check(proxy_instance)) { PyErr_SetString(PyExc_ValueError, "Expecting Proxy instance as argument"); return NULL; } if (!z_policy_proxy_bind_implementation(proxy_instance)) { PyErr_SetString(PyExc_RuntimeError, "Error binding proxy implementation"); return NULL; } if (z_proxy_group_start_session(proxy_group, z_policy_proxy_get_proxy(proxy_instance))) { return PyInt_FromLong(1); } return z_policy_none_ref(); } /** * z_policy_proxy_group_new_instance: * @o unused * @args Python arguments: * * Returns: * The new instance */ static ZPolicyObj * z_policy_proxy_group_new_instance(PyObject *o G_GNUC_UNUSED, PyObject *args) { gint max_sessions; ZProxyGroup *proxy_group; ZPolicyDict *dict; ZPolicyObj *res; if (!PyArg_Parse(args, "(i)", &max_sessions)) return NULL; proxy_group = z_proxy_group_new(max_sessions); dict = z_policy_dict_new(); /* NOTE: we need to add a reference to proxy_group here as our instance * might be freed earlier than the method reference, in a construct like * ProxyGroup(1).start(proxy). */ z_policy_dict_register(dict, Z_VT_METHOD, "start", Z_VF_READ, z_policy_proxy_group_start, proxy_group, NULL); z_policy_dict_set_app_data(dict, proxy_group, (GDestroyNotify) z_proxy_group_orphan); res = z_policy_struct_new(dict, Z_PST_PROXY_GROUP); return res; } PyMethodDef z_policy_proxy_group_funcs[] = { { "ProxyGroup", (PyCFunction) z_policy_proxy_group_new_instance, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; /** * z_policy_proxy_group_init: * * Module initialisation */ void z_policy_proxy_group_module_init(void) { Py_InitModule("Zorp.Zorp", z_policy_proxy_group_funcs); } zorp-3.9.5/lib/pysatyr.c000066400000000000000000000023421172670260400151550ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : bazsi * Last audited version: 1.38 * Notes: * ***************************************************************************/ zorp-3.9.5/lib/pysockaddr.c000066400000000000000000000277461172670260400156240ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : bazsi * Auditor : kisza * Last audited version: 1.4 * Notes: * ***************************************************************************/ #include #include #include #include /** * z_policy_sockaddr_inet_new_instance: * @s not used * @args Python args: ip, port * * SockAddr.SockAddrInet(ip, port), creates a new ZSockAddrInet. * * Returns: * The new instance */ static PyObject * z_policy_sockaddr_inet_new_instance(PyObject *s G_GNUC_UNUSED, PyObject *args) { ZSockAddr *sa; PyObject *res; gchar *ip; gint port; guint32 ip_addr; if (PyArg_Parse(args, "(si)", &ip, &port)) { sa = z_sockaddr_inet_new(ip, port); if (!sa) { PyErr_SetString(PyExc_ValueError, "Invalid IP address"); return NULL; } } else { PyErr_Clear(); if (PyArg_Parse(args, "(ii)", &ip_addr, &port)) { struct sockaddr_in socket; memset(&socket, 0, sizeof(socket)); socket.sin_family = AF_INET; socket.sin_addr.s_addr = htonl(ip_addr); socket.sin_port = htons(port); sa = z_sockaddr_inet_new2(&socket); if (!sa) { PyErr_SetString(PyExc_ValueError, "Invalid IP address"); return NULL; } } else { PyErr_SetString(PyExc_ValueError, "Invalid parameter"); return NULL; } } res = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); return res; } /** * z_policy_sockaddr_inet_new_hostname: * @s not used * @args Python args: ip, port * * SockAddr.SockAddrInetHostname(hostname port), creates a new ZSockAddrInet. * * Returns: * The new instance */ static PyObject * z_policy_sockaddr_inet_new_hostname(PyObject *s G_GNUC_UNUSED, PyObject *args) { ZSockAddr *sa; PyObject *res; gchar *hostname; gint port; if (PyArg_Parse(args, "(si)", &hostname, &port)) { sa = z_sockaddr_inet_new_hostname(hostname, port); if (!sa) { PyErr_SetString(PyExc_ValueError, "Invalid hostname"); return NULL; } } else { PyErr_SetString(PyExc_ValueError, "Invalid parameter"); return NULL; } res = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); return res; } /** * z_policy_sockaddr_inet_new_instance: * @s not used * @args Python args: ip, port_min, port_max * * Implementation of SockAddrInetRange(ip, port_min, port_max), creates a new * ZSockAddrInetRange. * * Returns: * The new instance */ static PyObject * z_policy_sockaddr_inet_range_new_instance(PyObject *s G_GNUC_UNUSED, PyObject *args) { PyObject *res; ZSockAddr *sa; gchar *ip; gint min_port, max_port; if (!PyArg_Parse(args, "(sii)", &ip, &min_port, &max_port)) return NULL; sa = z_sockaddr_inet_range_new(ip, min_port, max_port); if (!sa) { PyErr_SetString(PyExc_ValueError, "Invalid IP address"); return NULL; } res = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); return res; } /** * z_policy_sockaddr_inet6_new_instance: * @s not used * @args Python args: ip, port * * SockAddr.SockAddrInet6(ip, port), creates a new ZSockAddrInet6. * * Returns: * The new instance */ static PyObject * z_policy_sockaddr_inet6_new_instance(PyObject *s G_GNUC_UNUSED, PyObject *args) { PyObject *res; ZSockAddr *sa; gchar *ip; gint port; if (!PyArg_Parse(args, "(si)", &ip, &port)) return NULL; sa = z_sockaddr_inet6_new(ip, port); if (!sa) { PyErr_SetString(PyExc_ValueError, "Invalid IP address"); return NULL; } res = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); return res; } /** * z_policy_sockaddr_unix_new_instance: * @s not used * @args Python args: path * * SockAddr.SockAddrUnix(path), creates a new ZSockAddrUnix. * * Returns: * The new instance */ static PyObject * z_policy_sockaddr_unix_new_instance(PyObject *s G_GNUC_UNUSED, PyObject *args) { ZSockAddr *sa; PyObject *res; gchar *path; if (!PyArg_Parse(args, "(s)", &path)) return NULL; sa = z_sockaddr_unix_new(path); res = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); return res; } PyMethodDef z_policy_sockaddr_funcs[] = { { "SockAddrInet", z_policy_sockaddr_inet_new_instance, METH_VARARGS, NULL }, { "SockAddrInetRange", z_policy_sockaddr_inet_range_new_instance, METH_VARARGS, NULL }, { "SockAddrInetHostname", z_policy_sockaddr_inet_new_hostname, METH_VARARGS, NULL }, { "SockAddrInet6", z_policy_sockaddr_inet6_new_instance, METH_VARARGS, NULL }, { "SockAddrUnix", z_policy_sockaddr_unix_new_instance, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; /** * z_policy_sockaddr_format: * @self this * * Produce a human-readable dump of a ZSockAddr. * * Returns: * Python string containing the dump */ static ZPolicyObj * z_policy_sockaddr_format(gpointer user_data, ZPolicyObj *args, ZPolicyObj *kw G_GNUC_UNUSED) { ZSockAddr *sa = (ZSockAddr *) user_data; char buf[MAX_SOCKADDR_STRING]; if (!z_policy_var_parse(args, "()")) return NULL; return PyString_FromString(z_sockaddr_format(sa, buf, sizeof(buf))); } /** * z_policy_sockaddr_clone: * @self this * @args Python args: wild_flags * * SockAddr.clone, Copy-constructor of SockAddr. * * Returns: * New instance */ static ZPolicyObj * z_policy_sockaddr_clone(gpointer user_data, ZPolicyObj *args, ZPolicyObj *kw G_GNUC_UNUSED) { PyObject *res; gint wild; ZSockAddr *sa = (ZSockAddr *) user_data, *a; if (!z_policy_var_parse(args, "(i)", &wild)) return NULL; a = z_sockaddr_clone(sa, wild); res = z_policy_sockaddr_new(a); z_sockaddr_unref(a); return res; } /** * z_policy_sockaddr_equal: * @self this * @args Python args: wild_flags * * SockAddr.clone, Copy-constructor of SockAddr. * * Returns: * New instance */ static ZPolicyObj * z_policy_sockaddr_equal(gpointer user_data, ZPolicyObj *args, ZPolicyObj *kw G_GNUC_UNUSED) { PyObject *other_obj, *res; ZSockAddr *this_sa = (ZSockAddr *) user_data, *other_sa; if (!z_policy_var_parse(args, "(O)", &other_obj)) return NULL; if (!z_policy_sockaddr_check(other_obj)) { PyErr_SetString(PyExc_ValueError, "Argument must be a SockAddr instance"); return NULL; } other_sa = z_policy_sockaddr_get_sa(other_obj); res = PyInt_FromLong(z_sockaddr_equal(this_sa, other_sa)); z_sockaddr_unref(other_sa); return res; } ZSockAddr * z_policy_sockaddr_get_sa(ZPolicyObj *s) { ZSockAddr *sa; if (!z_policy_sockaddr_check(s)) return NULL; sa = (ZSockAddr *) z_policy_dict_get_app_data(z_policy_struct_get_dict(s)); return z_sockaddr_ref(sa); } static ZPolicyObj * z_policy_sockaddr_str(ZPolicyObj *s) { ZSockAddr *sa = z_policy_sockaddr_get_sa(s); ZPolicyObj *res; char buf[MAX_SOCKADDR_STRING]; res = PyString_FromString(z_sockaddr_format(sa, buf, sizeof(buf))); z_sockaddr_unref(sa); return res; } /** * z_policy_sockaddr_pack: * @self this * * SockAddr.pack, get a packed string representation of the address * * Returns: * A string representation of the address in network byte order packed format. * Returns None if the address family is not INET or INET6. */ static ZPolicyObj * z_policy_sockaddr_pack(gpointer user_data, ZPolicyObj *args G_GNUC_UNUSED, ZPolicyObj *kw G_GNUC_UNUSED) { ZSockAddr *sa = (ZSockAddr *) user_data; switch (sa->sa.sa_family) { case AF_INET: { struct sockaddr_in *sa_in = (struct sockaddr_in *) &sa->sa; return PyString_FromStringAndSize((gchar*)&sa_in->sin_addr, 4); break; } case AF_INET6: { struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) &sa->sa; return PyString_FromStringAndSize((gchar*)&sa_in6->sin6_addr, 16); break; } default: return z_policy_none_ref(); } } /** * z_policy_sockaddr_new: * @sa ZSockAddr address * * Constructor of ZPolicySockAddr, creates new instance by ZSockAddr. * * Returns: * The new instance */ ZPolicyObj * z_policy_sockaddr_new(ZSockAddr *sa) { ZPolicyDict *dict; ZPolicyObj *res; gint struct_type; dict = z_policy_dict_new(); z_policy_dict_register(dict, Z_VT_INT16, "family", Z_VF_READ | Z_VF_LITERAL, sa->sa.sa_family); z_policy_dict_register(dict, Z_VT_METHOD, "clone", Z_VF_READ, z_policy_sockaddr_clone, z_sockaddr_ref(sa), z_sockaddr_unref); z_policy_dict_register(dict, Z_VT_METHOD, "format", Z_VF_READ, z_policy_sockaddr_format, z_sockaddr_ref(sa), z_sockaddr_unref); z_policy_dict_register(dict, Z_VT_METHOD, "equal", Z_VF_READ, z_policy_sockaddr_equal, z_sockaddr_ref(sa), z_sockaddr_unref); switch (sa->sa.sa_family) { case AF_INET: { struct sockaddr_in *sa_in = (struct sockaddr_in *) &sa->sa; /* FIXME: inconsistency, port is reported in host byte order, * ip in network byte order */ z_policy_dict_register(dict, Z_VT_CSTRING, "type", Z_VF_READ | Z_VF_LITERAL, "inet", 0); z_policy_dict_register(dict, Z_VT_IP, "ip", Z_VF_RW, &sa_in->sin_addr); z_policy_dict_register(dict, Z_VT_IP, "ip_s", Z_VF_RW | Z_VF_IP_STR, &sa_in->sin_addr); z_policy_dict_register(dict, Z_VT_INT16, "port", Z_VF_RW | Z_VF_INT_NET, &sa_in->sin_port); z_policy_dict_register(dict, Z_VT_METHOD, "pack", Z_VF_READ, z_policy_sockaddr_pack, z_sockaddr_ref(sa), z_sockaddr_unref); struct_type = Z_PST_SOCKADDR_INET; break; } case AF_INET6: { struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) &sa->sa; z_policy_dict_register(dict, Z_VT_CSTRING, "type", Z_VF_READ | Z_VF_LITERAL, "inet", 0); z_policy_dict_register(dict, Z_VT_IP6, "ip", Z_VF_RW, &sa_in6->sin6_addr); z_policy_dict_register(dict, Z_VT_IP6, "ip_s", Z_VF_RW | Z_VF_IP_STR, &sa_in6->sin6_addr); z_policy_dict_register(dict, Z_VT_INT16, "port", Z_VF_RW | Z_VF_INT_NET, &sa_in6->sin6_port); z_policy_dict_register(dict, Z_VT_METHOD, "pack", Z_VF_READ, z_policy_sockaddr_pack, z_sockaddr_ref(sa), z_sockaddr_unref); struct_type = Z_PST_SOCKADDR_INET6; break; } case AF_UNIX: { struct sockaddr_un *sa_un = (struct sockaddr_un *) &sa->sa; z_policy_dict_register(dict, Z_VT_CSTRING, "type", Z_VF_READ | Z_VF_LITERAL, "unix", 0); z_policy_dict_register(dict, Z_VT_CSTRING, "path", Z_VF_RW, sa_un->sun_path, sizeof(sa_un->sun_path)); struct_type = Z_PST_SOCKADDR_UNIX; break; } default: z_policy_dict_destroy(dict); return NULL; } z_policy_dict_set_app_data(dict, z_sockaddr_ref(sa), (GDestroyNotify) z_sockaddr_unref); res = z_policy_struct_new(dict, struct_type); z_policy_struct_set_format(res, z_policy_sockaddr_str); return res; } /** * z_policy_sockaddr_init: * * Module initialisation */ void z_policy_sockaddr_module_init(void) { Py_InitModule("Zorp.SockAddr", z_policy_sockaddr_funcs); } zorp-3.9.5/lib/pystream.c000066400000000000000000000272021172670260400153100ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : kisza * Last audited version: 1.8 * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include PyObject *z_policy_stream_new(ZStream *Stream); static PyObject *z_policy_stream_new_instance(PyObject *self, PyObject *args); static void z_policy_stream_destroy(PyObject *o); static PyObject *z_policy_stream_getattr(PyObject *o, char *name); static gint z_policy_stream_setattr(PyObject *o, char *name, PyObject *value); static PyObject *z_policy_stream_repr(PyObject *o); static PyObject *z_policy_stream_read(PyObject *o, PyObject *args); static PyObject *z_policy_stream_write(PyObject *o, PyObject *args); static PyObject *z_policy_stream_close(PyObject *o, PyObject *args); static PyObject *z_policy_stream_readline(PyObject *o, PyObject *args); static PyObject *z_policy_stream_exception = NULL; PyMethodDef z_policy_stream_funcs[] = { { "Stream", z_policy_stream_new_instance, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; static PyMethodDef py_zorp_stream_methods[] = { { "read", z_policy_stream_read, METH_VARARGS, NULL }, { "write", z_policy_stream_write, METH_VARARGS, NULL }, { "close", (PyCFunction) z_policy_stream_close, 0, NULL }, { "readline", z_policy_stream_readline, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } /* sentinel*/ }; PyTypeObject z_policy_stream_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "ZPolicyStream", .tp_basicsize = sizeof(ZPolicyStream), .tp_dealloc = (destructor) z_policy_stream_destroy, .tp_getattr = (getattrfunc) z_policy_stream_getattr, .tp_setattr = (setattrfunc) z_policy_stream_setattr, .tp_repr = (reprfunc) z_policy_stream_repr, .tp_doc = "ZPolicyStream class for Zorp", }; /** * z_policy_stream_new: * @str: ZStream instance * * This function allocates a Python object representing the Zorp stream * @str. It is to be called from C code, the Python version of this * constructor is below. * * Returns: the newly allocated Python object **/ PyObject * z_policy_stream_new(ZStream *str) { ZPolicyStream *self; if (str == NULL) { /*LOG This message indicates an internal error, please contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 3, "Internal error in z_policy_stream_new: input ZStream is NULL;"); return NULL; } self = PyObject_New(ZPolicyStream, &z_policy_stream_type); z_stream_ref(str); self->stream = str; return (PyObject *) self; } /** * z_policy_stream_new: * @o: Python self argument * @args: Python args argument * * This function can be called from Python to create and initialize a new * stream with an underlying ZStreamFD. The function expects two arguments * from Python: (fd, name), where fd is the file descriptor to attach the * new stream to and name will be the name of this stream. * * Returns: the newly allocated Python object **/ static PyObject * z_policy_stream_new_instance(PyObject *o G_GNUC_UNUSED, PyObject *args) { ZPolicyStream *self; char *name; int fd; if (!PyArg_ParseTuple(args, "is", &fd, &name)) return NULL; self = PyObject_New(ZPolicyStream, &z_policy_stream_type); if (!self) return NULL; self->stream = z_stream_fd_new(fd, name); return (PyObject *) self; } /** * z_policy_stream_destroy: * @o: Python self argument * * This function is registered as the free function for ZPolicyStream, thus it is * called when a ZPolicyStream is to be freed. **/ static void z_policy_stream_destroy(PyObject *o) { ZPolicyStream *self = (ZPolicyStream *) o; z_stream_unref(self->stream); PyObject_Del(self); } /** * z_policy_stream_getattr: * @o: Python self argument * @name: attribute to return * * This function is called when an attribute from the ZPolicyStream object is * read, the support for various builtin attributes (fd, name etc.) is * implemented here. * * Returns: the Python object referring to the attribute **/ static PyObject * z_policy_stream_getattr(PyObject *o, char *name) { ZPolicyStream *self = (ZPolicyStream *) o; if (strcmp(name, "fd") == 0) { return z_policy_var_build("i", z_stream_get_fd(self->stream)); } else if (strcmp(name, "name") == 0) { return PyString_FromString(self->stream->name); } else if (strcmp(name, "bytes_recvd") == 0) { return PyLong_FromUnsignedLong(self->stream->bytes_recvd); } else if (strcmp(name, "bytes_sent") == 0) { return PyLong_FromUnsignedLong(self->stream->bytes_sent); } else if (strcmp(name, "nul_nonfatal") == 0) { gboolean value; z_stream_ctrl(self->stream, ZST_LINE_GET_NUL_NONFATAL, &value, sizeof(gboolean)); return Py_BuildValue("i", !!value); } else if (strcmp(name, "split") == 0) { gboolean value; z_stream_ctrl(self->stream, ZST_LINE_GET_SPLIT, &value, sizeof(gboolean)); return Py_BuildValue("i", value); } else if (strcmp(name, "keepalive") == 0) { return PyLong_FromLong(z_stream_get_keepalive(self->stream)); } return Py_FindMethod(py_zorp_stream_methods, o, name); } /** * z_policy_stream_setattr: * @o: Python self argument * @name: attribute to set * @value: new value for attribute * * This function is called when an attribute from the ZPolicyStream object is * written to, the support for various builtin attributes (fd, name etc.) is * implemented here. * * Returns: 1 to indicate failure, 0 for success **/ static gint z_policy_stream_setattr(PyObject *o, char *name, PyObject *value) { ZPolicyStream *self = (ZPolicyStream *) o; gchar *str; if (strcmp(name, "name") == 0) { if (!PyArg_Parse(value, "s", &str)) { PyErr_SetString(PyExc_TypeError, "Stream name is not a string"); return 1; } else { z_stream_set_name(self->stream, str); return 0; } } else if (strcmp(name, "nul_nonfatal") == 0) { int cval; if (!PyArg_Parse(value, "i", &cval)) { PyErr_SetString(PyExc_TypeError, "nul_nonfatal is boolean"); return 1; } z_stream_ctrl(self->stream, ZST_LINE_SET_NUL_NONFATAL, &cval, sizeof(int)); return 0; } else if (strcmp(name, "split") == 0) { int cval; if (!PyArg_Parse(value, "i", &cval)) { PyErr_SetString(PyExc_TypeError, "split is boolean"); return 1; } z_stream_ctrl(self->stream, ZST_LINE_SET_SPLIT, &cval, sizeof(int)); return 0; } else if (strcmp(name, "keepalive") == 0) { gint keepalive; if (!PyArg_Parse(value, "i", &keepalive)) { PyErr_SetString(PyExc_TypeError, "Stream keepalive value is not an integer"); return 1; } else { z_stream_set_keepalive(self->stream, keepalive); return 0; } } PyErr_SetString(PyExc_AttributeError, "No such attribute"); return 1; } /** * z_policy_stream_repr: * @o: ZPolicyStream object * * __repr__ function for ZPolicyStream objects **/ static PyObject * z_policy_stream_repr(PyObject *o) { ZPolicyStream *self = (ZPolicyStream *) o; return PyString_FromString(self->stream->name); } /** * z_policy_stream_readline * @o: Python self, ZPolicyStream object * @args: Python args argument * * readline method exported to Python with this declaration: * def readline(self) * * gets a line from the stream */ static PyObject * z_policy_stream_readline(PyObject *o, PyObject *args G_GNUC_UNUSED) { ZPolicyStream *self = (ZPolicyStream *) o; gchar *buf; PyObject *pybuf; gsize bytes_read; gint res; Py_BEGIN_ALLOW_THREADS res = z_stream_line_get(self->stream, &buf, &bytes_read, NULL); Py_END_ALLOW_THREADS if (res == G_IO_STATUS_NORMAL) { pybuf = Py_BuildValue("s#", buf, bytes_read); return pybuf; } PyErr_SetObject(z_policy_stream_exception, Py_BuildValue("(i,O)", res, Py_None)); return NULL; } /** * z_policy_stream_read: * @o: Python self, ZPolicyStream object * @args: Python args argument * * read method exported to Python with this declaration: * def read(length): * * the length argument specifies how many bytes need to be read. **/ static PyObject * z_policy_stream_read(PyObject *o, PyObject *args) { ZPolicyStream *self = (ZPolicyStream *) o; PyObject *pybuf; gchar *buf; guint length; gsize bytes_read; gint res; if (!PyArg_ParseTuple(args, "i", &length)) return NULL; buf = g_new0(char, length); Py_BEGIN_ALLOW_THREADS res = z_stream_read(self->stream, buf, length, &bytes_read, NULL); Py_END_ALLOW_THREADS if (res == G_IO_STATUS_NORMAL) { pybuf = Py_BuildValue("s#", buf, bytes_read); g_free(buf); return pybuf; } g_free(buf); PyErr_SetObject(z_policy_stream_exception, Py_BuildValue("(i,O)", res, Py_None)); return NULL; } /** * z_policy_stream_write: * @o: Python self, ZPolicyStream object * @args: Python args argument * * read method exported to Python with this declaration: * def read(buf): * * the buf argument is a Python string which contains the byte sequence to * be written. **/ static PyObject * z_policy_stream_write(PyObject *o, PyObject *args) { ZPolicyStream *self = (ZPolicyStream *) o; gchar *buf; guint length; gsize bytes_written; gint res; if (!PyArg_ParseTuple(args, "s#", &buf, &length)) return NULL; Py_BEGIN_ALLOW_THREADS res = z_stream_write(self->stream, buf, length, &bytes_written, NULL); Py_END_ALLOW_THREADS if (res != G_IO_STATUS_NORMAL) { PyErr_SetString(PyExc_IOError, "I/O error writing stream."); return NULL; } z_return(z_policy_none_ref()); } /** * z_policy_stream_close: * @o: Python self argument, ZPolicyStream object * @args: Python args argument * * Close method exported to Python. **/ static PyObject * z_policy_stream_close(PyObject *o, PyObject *args G_GNUC_UNUSED) { ZPolicyStream *self = (ZPolicyStream *) o; z_stream_close(self->stream, NULL); z_return(z_policy_none_ref()); } /** * z_policy_stream_init: * * This function is called at Python initialization to export ZPolicyStream * related functions. **/ void z_policy_stream_module_init(void) { PyObject* module; PyImport_AddModule("Zorp.Stream"); module = Py_InitModule("Zorp.Stream", z_policy_stream_funcs); z_policy_stream_exception = PyErr_NewException("Zorp.Stream.StreamException", NULL, NULL); Py_INCREF(z_policy_stream_exception); PyModule_AddObject(module, "StreamException", z_policy_stream_exception); } zorp-3.9.5/lib/pystruct.c000066400000000000000000000172251172670260400153450ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include /* a generic wrapper for ZPolicyDict */ struct _ZPolicyStruct { PyObject_HEAD ZPolicyDict *dict; ZPolicyStructFormatFunc format_func; gboolean is_config; }; extern PyTypeObject z_policy_struct_types[Z_PST_MAX]; static void z_policy_struct_free(ZPolicyObj *s); void z_policy_struct_set_is_config(ZPolicyObj *s, gboolean is_config) { ZPolicyStruct *self = (ZPolicyStruct *) s; self->is_config = is_config; } gboolean z_policy_struct_check(ZPolicyObj *s, gint type) { if (type) return s->ob_type == &z_policy_struct_types[type]; return (void *) s->ob_type->tp_dealloc == (void *) z_policy_struct_free; } ZPolicyDict * z_policy_struct_release_dict(ZPolicyObj *s) { ZPolicyStruct *self = (ZPolicyStruct *) s; ZPolicyDict *dict; g_assert(z_policy_struct_check(s, 0)); dict = self->dict; z_policy_dict_unwrap(dict, s); self->dict = NULL; return dict; } ZPolicyDict * z_policy_struct_get_dict(ZPolicyObj *s) { ZPolicyStruct *self = (ZPolicyStruct *) s; g_assert(z_policy_struct_check(s, 0)); return self->dict; } void z_policy_struct_set_format(ZPolicyObj *s, ZPolicyStructFormatFunc format) { ZPolicyStruct *self = (ZPolicyStruct *) s; z_policy_struct_check(s, Z_PST_NONE); self->format_func = format; } static PyObject * z_policy_struct_str(ZPolicyObj *s) { ZPolicyStruct *self = (ZPolicyStruct *) s; if (self->format_func) { return self->format_func(s); } else { gchar buf[128]; g_snprintf(buf, sizeof(buf), "ZPolicyStruct object type %s", self->ob_type->tp_name); return PyString_FromString(buf); } } static ZPolicyObj * z_policy_struct_getattr(ZPolicyObj *s, gchar *name) { ZPolicyStruct *self = (ZPolicyStruct *) s; ZPolicyObj *res; if (strcmp(name, "__dict__") == 0) { return z_policy_dict_get_dict(self->dict); } else { res = z_policy_dict_get_value(self->dict, self->is_config, name); if (!res) z_policy_raise_exception_obj(PyExc_AttributeError, "No such attribute"); } return res; } static gint z_policy_struct_setattr(ZPolicyObj *s, gchar *name, ZPolicyObj *new_value) { ZPolicyStruct *self = (ZPolicyStruct *) s; gint res; res = z_policy_dict_set_value(self->dict, self->is_config, name, new_value); if (res < 0 && !PyErr_Occurred()) z_policy_raise_exception_obj(PyExc_AttributeError, "Error writing attribute"); else if (res > 0) { /* not found in dict, create a new entry */ z_policy_dict_register(self->dict, Z_VT_OBJECT, name, Z_VF_RW | Z_VF_CFG_RW | Z_VF_LITERAL | Z_VF_CONSUME, new_value); z_policy_var_ref(new_value); res = 0; } return (res != 0); } ZPolicyObj * z_policy_struct_new(ZPolicyDict *dict, gint type) { ZPolicyStruct *self; g_assert(type > Z_PST_NONE && type < Z_PST_MAX); self = PyObject_New(ZPolicyStruct, &z_policy_struct_types[type]); if (!self) return NULL; self->dict = dict; self->format_func = NULL; self->is_config = FALSE; z_policy_dict_wrap(dict, (ZPolicyObj *) self); return (ZPolicyObj *) self; } static void z_policy_struct_free(ZPolicyObj *s) { ZPolicyStruct *self = (ZPolicyStruct *) s; if (self->dict) { z_policy_dict_unwrap(self->dict, s); z_policy_dict_destroy(self->dict); } PyObject_Del(self); } void z_policy_struct_module_init(void) { static struct { gchar *name; gint parent_type; } types[] = { [Z_PST_NONE] = { "Unknown" , -1 }, [Z_PST_SHARED] = { "Shared", -1 }, [Z_PST_SOCKADDR] = { "SockAddr", -1 }, [Z_PST_SOCKADDR_INET] = { "SockAddrInet", Z_PST_SOCKADDR }, [Z_PST_SOCKADDR_INET6] = { "SockAddrInet6", Z_PST_SOCKADDR }, [Z_PST_SOCKADDR_UNIX] = { "SockAddrUnix", Z_PST_SOCKADDR }, [Z_PST_DISPATCH_BIND] = { "DispatchBind", -1 }, [Z_PST_DB_SOCKADDR] = { "DBSockAddr", Z_PST_DISPATCH_BIND }, [Z_PST_DB_IFACE] = { "DBIface", Z_PST_DISPATCH_BIND }, [Z_PST_DB_IFACE_GROUP] = { "DBIfaceGroup", Z_PST_DISPATCH_BIND }, [Z_PST_PROXY_GROUP] = { "ProxyGroup", -1 }, }; ZPolicyObj *m; gint i; m = PyImport_AddModule("Zorp.Zorp"); for (i = Z_PST_NONE + 1; i < Z_PST_MAX; i++) { gchar type_ref[64]; g_assert(types[i].name); if (!z_policy_struct_types[i].tp_repr) { memcpy(&z_policy_struct_types[i], &z_policy_struct_types[Z_PST_NONE], sizeof(z_policy_struct_types[Z_PST_NONE])); z_policy_struct_types[i].tp_name = types[i].name; z_policy_struct_types[i].tp_doc = types[i].name; if (types[i].parent_type != -1) { z_policy_var_ref((ZPolicyObj *) &z_policy_struct_types[types[i].parent_type]); z_policy_struct_types[i].tp_base = &z_policy_struct_types[types[i].parent_type]; } } PyType_Ready(&z_policy_struct_types[i]); Py_INCREF(&z_policy_struct_types[i]); g_snprintf(type_ref, sizeof(type_ref), "%sType", types[i].name); PyModule_AddObject(m, type_ref, (ZPolicyObj *) &z_policy_struct_types[i]); } } PyTypeObject z_policy_struct_types[Z_PST_MAX] = { [Z_PST_NONE] = { /* used as a template type for other types */ PyObject_HEAD_INIT(&PyType_Type) .ob_size = 0, .tp_name = "ZPolicyStruct", .tp_basicsize = sizeof(ZPolicyStruct), .tp_itemsize = 0, .tp_dealloc = (destructor)z_policy_struct_free, .tp_print = NULL, .tp_getattr = (getattrfunc) z_policy_struct_getattr, .tp_setattr = (setattrfunc) z_policy_struct_setattr, .tp_compare = NULL, .tp_repr = z_policy_struct_str, .tp_as_number = NULL, .tp_as_sequence = NULL, .tp_as_mapping = NULL, .tp_hash = NULL, .tp_call = NULL, .tp_str = z_policy_struct_str, .tp_getattro = NULL, .tp_setattro = NULL, .tp_as_buffer = NULL, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_doc = "ZPolicyProxy class", .tp_traverse = NULL, .tp_clear = NULL, .tp_richcompare = NULL, .tp_weaklistoffset = 0, .tp_iter = NULL, .tp_iternext = NULL, .tp_methods = NULL, .tp_members = NULL, .tp_getset = NULL, .tp_base = NULL, .tp_dict = NULL, .tp_descr_get = NULL, .tp_descr_set = NULL, .tp_dictoffset = 0, .tp_init = NULL, .tp_alloc = NULL, .tp_new = PyType_GenericNew, .tp_free = NULL, .tp_is_gc = NULL, .tp_bases = NULL, .tp_mro = NULL, .tp_cache = NULL, .tp_subclasses = NULL, .tp_weaklist = NULL, .tp_del = NULL, } }; zorp-3.9.5/lib/pyx509.c000066400000000000000000000405771172670260400145340ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Bazsi, Panther * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #define PROXY_SSL_EXTRACT_PEM(s, l, r) \ ({ void *p; BIO *bio = BIO_new_mem_buf(s, l); p = r(bio, NULL, NULL, NULL); BIO_free(bio); p; }) typedef struct _ZorpCertificate { PyObject_HEAD X509 *cert; } ZorpCertificate; static PyTypeObject z_py_zorp_certificate_type; static PyObject * z_py_zorp_certificate_new(X509 *cert) { ZorpCertificate *self; if (cert) { self = PyObject_New(ZorpCertificate, &z_py_zorp_certificate_type); self->cert = cert; CRYPTO_add(&cert->references,1,CRYPTO_LOCK_X509); return (PyObject *) self; } else { return z_policy_none_ref(); } } static PyObject * z_py_zorp_certificate_getattr(PyObject *o, char *name) { ZorpCertificate *self = (ZorpCertificate *) o; PyObject *res = NULL; BIO *bio; guint len; gchar *mem; gchar buf[512]; if (strcmp(name, "blob") == 0) { bio = BIO_new(BIO_s_mem()); PEM_write_bio_X509(bio, self->cert); len = BIO_get_mem_data(bio, &mem); res = PyString_FromStringAndSize(mem, len); BIO_free(bio); } else if (strcmp(name, "issuer") == 0) { X509_NAME_oneline(X509_get_issuer_name(self->cert), buf, sizeof(buf)); res = PyString_FromString(buf); } else if (strcmp(name, "subject") == 0) { X509_NAME_oneline(X509_get_subject_name(self->cert), buf, sizeof(buf)); res = PyString_FromString(buf); } else if (strcmp(name, "serial") == 0) { ASN1_INTEGER *cert_serial; cert_serial = X509_get_serialNumber(self->cert); if (cert_serial) { res = PyInt_FromLong(ASN1_INTEGER_get(cert_serial)); } } else { PyErr_SetString(PyExc_AttributeError, "Attribute not found"); } return res; } static void z_py_zorp_certificate_free(ZorpCertificate *self) { X509_free(self->cert); PyObject_Del(self); } static PyTypeObject z_py_zorp_certificate_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp Certificate", .tp_basicsize = sizeof(ZorpCertificate), .tp_dealloc = (destructor) z_py_zorp_certificate_free, .tp_getattr = z_py_zorp_certificate_getattr, /* tp_getattr */ .tp_doc = "ZorpCertificate class for Zorp", /* docstring */ }; typedef struct _ZorpCRL { PyObject_HEAD X509_CRL *crl; } ZorpCRL; static PyTypeObject z_py_zorp_crl_type; static PyObject * z_py_zorp_crl_new(X509_CRL *crl) { ZorpCRL *self; self = PyObject_New(ZorpCRL, &z_py_zorp_crl_type); self->crl = crl; CRYPTO_add(&crl->references,1,CRYPTO_LOCK_X509_CRL); return (PyObject *) self; } static PyObject * z_py_zorp_crl_getattr(PyObject *o, char *name) { ZorpCRL *self = (ZorpCRL *) o; PyObject *res = NULL; BIO *bio; guint len; gchar *mem; gchar buf[512]; if (strcmp(name, "blob") == 0) { bio = BIO_new(BIO_s_mem()); PEM_write_bio_X509_CRL(bio, self->crl); len = BIO_get_mem_data(bio, &mem); res = PyString_FromStringAndSize(mem, len); BIO_free(bio); } else if (strcmp(name, "issuer") == 0) { X509_NAME_oneline(X509_CRL_get_issuer(self->crl), buf, sizeof(buf)); res = PyString_FromString(buf); } else { PyErr_SetString(PyExc_AttributeError, "Attribute not found"); } return res; } static void z_py_zorp_crl_free(ZorpCRL *self) { X509_CRL_free(self->crl); PyObject_Del(self); } static PyTypeObject z_py_zorp_crl_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp CRL", .tp_basicsize = sizeof(ZorpCRL), .tp_dealloc = (destructor) z_py_zorp_crl_free, .tp_getattr = z_py_zorp_crl_getattr, .tp_doc = "ZorpCRL class for Zorp" }; typedef struct _ZorpCertList { PyObject_HEAD STACK_OF(X509) *certs; } ZorpCertList; static PyTypeObject z_py_zorp_cert_list_type; static PyObject * z_py_zorp_cert_list_new(STACK_OF(X509) *certs) { ZorpCertList *self; self = PyObject_New(ZorpCertList, &z_py_zorp_cert_list_type); self->certs = certs; return (PyObject *) self; } static void z_py_zorp_cert_list_free(ZorpCertList *self) { PyObject_Del(self); } static Py_ssize_t z_py_zorp_cert_list_length(ZorpCertList *self) { return sk_X509_num(self->certs); } static int z_py_zorp_cert_list_lookup(ZorpCertList *self, PyObject *ndx) { if (PyInt_Check(ndx)) { /* number */ if (PyInt_AsLong(ndx) >= 0 && PyInt_AsLong(ndx) < sk_X509_num(self->certs)) { return PyInt_AsLong(ndx); } } else if (PyString_Check(ndx)) { gchar buf[512]; int i; for (i = 0; i < sk_X509_num(self->certs); i++) { X509_NAME_oneline(X509_get_subject_name(sk_X509_value(self->certs, i)), buf, sizeof(buf)); if (strcmp(buf, PyString_AsString(ndx)) == 0) { return i; } } } return -1; } static PyObject * z_py_zorp_cert_list_subscript(ZorpCertList *self, PyObject *ndx) { int i; i = z_py_zorp_cert_list_lookup(self, ndx); if (i == -1) { PyErr_SetString(PyExc_KeyError, "Certificate not found."); return NULL; } return z_py_zorp_certificate_new(sk_X509_value(self->certs, i)); } static gint z_py_zorp_cert_list_ass_subscript(ZorpCertList *self, PyObject *ndx, PyObject *new) { X509 *cert = NULL; int i; if (new) { if (PyString_Check(new)) { /* new-ban pem, berakni az i. helyere */ cert = PROXY_SSL_EXTRACT_PEM(PyString_AsString(new), PyString_Size(new), PEM_read_bio_X509); } if (!cert) { PyErr_SetString(PyExc_TypeError, "Certificates must be specified as strings in PEM format"); return -1; } } i = z_py_zorp_cert_list_lookup(self, ndx); if (i != -1) { X509 *p = sk_X509_delete(self->certs, i); X509_free(p); } if (cert) { if (X509_find_by_subject(self->certs, X509_get_subject_name(cert))) { X509_free(cert); PyErr_SetString(PyExc_ValueError, "Trying to add a duplicate certificate."); return -1; } sk_X509_push(self->certs, cert); } return 0; } static PyMappingMethods z_py_zorp_cert_list_mapping = { (Z_PYMAPPING_LENFUNC_TYPE) z_py_zorp_cert_list_length, (binaryfunc) z_py_zorp_cert_list_subscript, (objobjargproc) z_py_zorp_cert_list_ass_subscript }; static PyTypeObject z_py_zorp_cert_list_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp Certificate List", .tp_basicsize = sizeof(ZorpCertList), .tp_dealloc = (destructor) z_py_zorp_cert_list_free, .tp_as_mapping = &z_py_zorp_cert_list_mapping, /* tp_as_mapping */ .tp_doc = "ZorpCertList class for Zorp", /* docstring */ }; typedef struct _ZorpCertNameList { PyObject_HEAD STACK_OF(X509_NAME) *cert_names; } ZorpCertNameList; static PyTypeObject z_py_zorp_cert_name_list_type; static PyObject * z_py_zorp_cert_name_list_new(STACK_OF(X509_NAME) *cert_names) { ZorpCertNameList *self; self = PyObject_New(ZorpCertNameList, &z_py_zorp_cert_name_list_type); self->cert_names = cert_names; return (PyObject *) self; } static void z_py_zorp_cert_name_list_free(ZorpCertNameList *self) { PyObject_Del(self); } static Py_ssize_t z_py_zorp_cert_name_list_length(ZorpCertNameList *self) { return sk_X509_NAME_num(self->cert_names); } static int z_py_zorp_cert_name_list_lookup(ZorpCertNameList *self, PyObject *ndx) { if (PyInt_Check(ndx)) { /* number */ if (PyInt_AsLong(ndx) >= 0 && PyInt_AsLong(ndx) < sk_X509_NAME_num(self->cert_names)) { return PyInt_AsLong(ndx); } } else if (PyString_Check(ndx)) { gchar buf[512]; int i, num; num = sk_X509_NAME_num(self->cert_names); for (i = 0; i < num; i++) { X509_NAME_oneline(sk_X509_NAME_value(self->cert_names, i), buf, sizeof(buf)); if (strcmp(buf, PyString_AsString(ndx)) == 0) { return i; } } } return -1; } static PyObject * z_py_zorp_cert_name_list_subscript(ZorpCertNameList *self, PyObject *ndx) { gchar buf[1024]; int i; i = z_py_zorp_cert_name_list_lookup(self, ndx); if (i == -1) { PyErr_SetString(PyExc_KeyError, "Certificate not found."); return NULL; } /* FIXME: return it as a string */ X509_NAME_oneline(sk_X509_NAME_value(self->cert_names, i), buf, sizeof(buf)); return PyString_FromString(buf); } static PyMappingMethods z_py_zorp_cert_name_list_mapping = { (Z_PYMAPPING_LENFUNC_TYPE) z_py_zorp_cert_name_list_length, (binaryfunc) z_py_zorp_cert_name_list_subscript, (objobjargproc) NULL }; static PyTypeObject z_py_zorp_cert_name_list_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp Certificate Name List", .tp_basicsize = sizeof(ZorpCertNameList), .tp_dealloc = (destructor) z_py_zorp_cert_name_list_free, .tp_as_mapping = &z_py_zorp_cert_name_list_mapping, .tp_doc = "ZorpCertNameList class for Zorp" }; typedef struct _ZorpCRLList { PyObject_HEAD STACK_OF(X509_CRL) *crls; } ZorpCRLList; static PyTypeObject z_py_zorp_crl_list_type; static PyObject * z_py_zorp_crl_list_new(STACK_OF(X509_CRL) *crls) { ZorpCRLList *self; self = PyObject_New(ZorpCRLList, &z_py_zorp_crl_list_type); self->crls = crls; return (PyObject *) self; } static void z_py_zorp_crl_list_free(ZorpCRLList *self) { PyObject_Del(self); } static Py_ssize_t z_py_zorp_crl_list_length(ZorpCRLList *self) { return sk_X509_CRL_num(self->crls); } static int z_py_zorp_crl_list_lookup(ZorpCRLList *self, PyObject *ndx) { if (PyInt_Check(ndx)) { /* number */ if (PyInt_AsLong(ndx) >= 0 && PyInt_AsLong(ndx) < sk_X509_CRL_num(self->crls)) { return PyInt_AsLong(ndx); } } else if (PyString_Check(ndx)) { gchar buf[512]; int i; for (i = 0; i < sk_X509_CRL_num(self->crls); i++) { X509_NAME_oneline(X509_CRL_get_issuer(sk_X509_CRL_value(self->crls, i)), buf, sizeof(buf)); if (strcmp(buf, PyString_AsString(ndx)) == 0) { return i; } } } return -1; } static PyObject * z_py_zorp_crl_list_subscript(ZorpCRLList *self, PyObject *ndx) { int i; i = z_py_zorp_crl_list_lookup(self, ndx); if (i == -1) { PyErr_SetString(PyExc_KeyError, "Certificate not found."); return NULL; } return z_py_zorp_crl_new(sk_X509_CRL_value(self->crls, i)); } static gint z_py_zorp_crl_list_ass_subscript(ZorpCRLList *self, PyObject *ndx, PyObject *new) { X509_CRL *crl = NULL; int i; if (new) { if (PyString_Check(new)) { /* new-ban pem, berakni az i. helyere */ crl = PROXY_SSL_EXTRACT_PEM(PyString_AsString(new), PyString_Size(new), PEM_read_bio_X509_CRL); } if (!crl) { PyErr_SetString(PyExc_TypeError, "CRLs must be specified as strings in PEM format"); return -1; } } i = z_py_zorp_crl_list_lookup(self, ndx); if (i != -1) { X509_CRL *p = sk_X509_CRL_delete(self->crls, i); X509_CRL_free(p); } if (crl) { #if 0 if (X509_CRL_find_by_subject(self->crls, X509_CRL_get_issuer(crl))) { X509_CRL_free(cert); PyErr_SetString(PyExc_ValueError, "Trying to add a duplicate certificate."); return -1; } #endif sk_X509_CRL_push(self->crls, crl); } return 0; } static PyMappingMethods z_py_zorp_crl_list_mapping = { (Z_PYMAPPING_LENFUNC_TYPE) z_py_zorp_crl_list_length, (binaryfunc) z_py_zorp_crl_list_subscript, (objobjargproc) z_py_zorp_crl_list_ass_subscript }; static PyTypeObject z_py_zorp_crl_list_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "Zorp CRL List", .tp_basicsize = sizeof(ZorpCRLList), .tp_dealloc = (destructor) z_py_zorp_crl_list_free, .tp_as_mapping = &z_py_zorp_crl_list_mapping, .tp_doc = "ZorpCRLList class for Zorp" }; ZPolicyObj * z_py_ssl_certificate_get(ZProxy *self G_GNUC_UNUSED, gchar *name G_GNUC_UNUSED, gpointer value) { X509 **cert = (X509 **) value; return z_py_zorp_certificate_new(*cert); } int z_py_ssl_certificate_set(ZProxy *self G_GNUC_UNUSED, gchar *name G_GNUC_UNUSED, gpointer value, ZPolicyObj *new) { X509 **cert = (X509 **) value; if (*cert) { X509_free(*cert); *cert = NULL; } if (PyString_Check(new)) { (*cert) = PROXY_SSL_EXTRACT_PEM(PyString_AsString(new), PyString_Size(new), PEM_read_bio_X509); } if (!(*cert)) { PyErr_SetString(PyExc_TypeError, "Certificates must be specified as strings in PEM format."); return -1; } return 0; } void z_py_ssl_certificate_free(gpointer value) { X509 **cert = (X509 **) value; X509_free(*cert); } ZPolicyObj * z_py_ssl_privkey_get(ZProxy *self G_GNUC_UNUSED, gchar *name G_GNUC_UNUSED, gpointer value G_GNUC_UNUSED) { return PyString_FromString("Private key retrieval is not supported."); } int z_py_ssl_privkey_set(ZProxy *self, gchar *name G_GNUC_UNUSED, gpointer value, ZPolicyObj *new) { EVP_PKEY **pkey = (EVP_PKEY **) value; GString *passphrase; z_proxy_enter(self); if (*pkey) { EVP_PKEY_free(*pkey); *pkey = NULL; } if (PyString_Check(new)) { if (pkey == &self->ssl_opts.local_privkey[EP_CLIENT]) passphrase = self->ssl_opts.local_privkey_passphrase[EP_CLIENT]; else if (pkey == &self->ssl_opts.local_privkey[EP_SERVER]) passphrase = self->ssl_opts.local_privkey_passphrase[EP_SERVER]; else passphrase = NULL; /* (*pkey) = PROXY_SSL_EXTRACT_PEM(PyString_AsString(new), PyString_Size(new), PEM_read_bio_PrivateKey); */ { BIO *bio = BIO_new_mem_buf(PyString_AsString(new), PyString_Size(new)); (*pkey) = PEM_read_bio_PrivateKey(bio, NULL, NULL, passphrase ? passphrase->str : NULL); BIO_free(bio); } } if (!(*pkey)) { PyErr_SetString(PyExc_TypeError, "Private keys must be specified as strings in PEM format."); z_proxy_return(self, -1); } z_proxy_return(self, 0); } void z_py_ssl_privkey_free(gpointer value) { EVP_PKEY **pkey = (EVP_PKEY **) value; EVP_PKEY_free(*pkey); } ZPolicyObj * z_py_ssl_cert_list_get(ZProxy *self G_GNUC_UNUSED, gchar *name G_GNUC_UNUSED, gpointer value) { STACK_OF(X509) **certlist = (STACK_OF(X509) **) value; return z_py_zorp_cert_list_new(*certlist); } void z_py_ssl_cert_list_free(gpointer value) { STACK_OF(X509) **certlist = (STACK_OF(X509) **) value; sk_X509_pop_free(*certlist, X509_free); } ZPolicyObj * z_py_ssl_cert_name_list_get(ZProxy *self G_GNUC_UNUSED, gchar *name G_GNUC_UNUSED, gpointer value) { STACK_OF(X509_NAME) **certnamelist = (STACK_OF(X509_NAME) **) value; return z_py_zorp_cert_name_list_new(*certnamelist); } void z_py_ssl_cert_name_list_free(gpointer value) { STACK_OF(X509_NAME) **certnamelist = (STACK_OF(X509_NAME) **) value; sk_X509_NAME_pop_free(*certnamelist, X509_NAME_free); } ZPolicyObj * z_py_ssl_crl_list_get(ZProxy *self G_GNUC_UNUSED, gchar *name G_GNUC_UNUSED, gpointer value) { STACK_OF(X509_CRL) **crllist = (STACK_OF(X509_CRL) **) value; return z_py_zorp_crl_list_new(*crllist); } void z_py_ssl_crl_list_free(gpointer value) { STACK_OF(X509_CRL) **crllist = (STACK_OF(X509_CRL) **) value; sk_X509_CRL_pop_free(*crllist, X509_CRL_free); } zorp-3.9.5/lib/pyzasauth.c000066400000000000000000000023301172670260400154670ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : * Last audited version: * Notes: * ***************************************************************************/ zorp-3.9.5/lib/satyr.c000066400000000000000000000023271172670260400146070ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : * Last audited version: * Notes: * ***************************************************************************/ zorp-3.9.5/lib/sysdep.c000066400000000000000000000115371172670260400147570ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #if ENABLE_NETFILTER_TPROXY #define in_tproxy in_tproxy_v12 #include #undef in_tproxy #include #endif /** * z_sysdep_parse_tproxy_arg: * @sysdep_tproxy_arg: --tproxy argument passed to Zorp * * This function converts the --tproxy argument to an internal Z_SD_TPROXY_* * representation. * * Returns: one of the Z_SD_TPROXY_* values **/ static gint z_sysdep_parse_tproxy_arg(const gchar *sysdep_tproxy_arg) { if (sysdep_tproxy_arg) { if (strcasecmp(sysdep_tproxy_arg, "tproxy40") == 0) { return Z_SD_TPROXY_NETFILTER_V40; } else if (strcasecmp(sysdep_tproxy_arg, "tproxy30") == 0) { return Z_SD_TPROXY_NETFILTER_V30; } else if (strcasecmp(sysdep_tproxy_arg, "netfilterv2") == 0 || strcasecmp(sysdep_tproxy_arg, "tproxy20") == 0) { return Z_SD_TPROXY_NETFILTER_V20; } else if (strcasecmp(sysdep_tproxy_arg, "netfilter") == 0 || strcasecmp(sysdep_tproxy_arg, "tproxy12") == 0) { return Z_SD_TPROXY_NETFILTER_V12; } else if (strcasecmp(sysdep_tproxy_arg, "linux22") == 0) { return Z_SD_TPROXY_LINUX22; } else if (strcasecmp(sysdep_tproxy_arg, "ipf") == 0) { return Z_SD_TPROXY_IPF; } } return 0; } /** * z_sysdep_init: * @sysdep_tproxy_arg: --tproxy argument passed to Zorp * * Initialize runtime system detection, currently it boils down to detecting * kernel support for transparent proxying. * * Returns: TRUE to indicate success **/ gboolean z_sysdep_init(const gchar *sysdep_tproxy_arg) { const gchar *sysdep_tproxy_str[] = { [0] = NULL, [Z_SD_TPROXY_LINUX22] = "linux22", [Z_SD_TPROXY_NETFILTER_V12] = "tproxy12", [Z_SD_TPROXY_IPF] = "ipf", [Z_SD_TPROXY_NETFILTER_V20] = "tproxy20", [Z_SD_TPROXY_NETFILTER_V30] = "tproxy30", [Z_SD_TPROXY_NETFILTER_V40] = "tproxy40" }; gint sysdep_tproxy = z_sysdep_parse_tproxy_arg(sysdep_tproxy_arg); if (system("/sbin/modprobe iptable_tproxy >/dev/null 2>&1") == -1) return FALSE; if (sysdep_tproxy == 0) { #if ENABLE_NETFILTER_TPROXY /* FIXME: we'd need a way to discover tproxy 4.0 is indeed there, as * it currently is tproxy below 4.0 will never be detected */ sysdep_tproxy = Z_SD_TPROXY_NETFILTER_V40; if (sysdep_tproxy == 0) { struct in_tproxy itp; socklen_t size = sizeof(itp); gint sock; sysdep_tproxy = Z_SD_TPROXY_LINUX22; sock = socket(PF_INET, SOCK_STREAM, 0); if (sock != -1) { memset(&itp, 0, sizeof(itp)); itp.op = TPROXY_VERSION; itp.v.version = 0x03000000; if (setsockopt(sock, SOL_IP, IP_TPROXY, &itp, size) == 0) { sysdep_tproxy = Z_SD_TPROXY_NETFILTER_V30; } else { /* not TProxy 3.0 */ itp.op = TPROXY_VERSION; itp.v.version = 0x02000000; if (setsockopt(sock, SOL_IP, IP_TPROXY, &itp, size) == -1) { /* not TProxy 2.0 */ guint flags; socklen_t flagslen = sizeof(flags); if (getsockopt(sock, SOL_IP, IP_TPROXY_FLAGS, &flags, &flagslen) == -1) { if (errno != ENOPROTOOPT) sysdep_tproxy = Z_SD_TPROXY_NETFILTER_V12; } else sysdep_tproxy = Z_SD_TPROXY_NETFILTER_V12; } else { sysdep_tproxy = Z_SD_TPROXY_NETFILTER_V30; } } close(sock); } } #elif ENABLE_LINUX22_TPROXY sysdep_tproxy = Z_SD_TPROXY_LINUX22; #else #error "No known tproxy support" #endif } /*LOG This message reports that system dependant was successful, tproxy support was set. */ z_log(NULL, CORE_DEBUG, 6, "System dependant init; sysdep_tproxy='%s'", sysdep_tproxy_str[sysdep_tproxy]); if (!z_dgram_init(sysdep_tproxy)) return FALSE; if (!z_tp_socket_init(sysdep_tproxy)) return FALSE; return TRUE; } void z_sysdep_destroy(void) { } zorp-3.9.5/lib/szig.c000066400000000000000000002020371172670260400144210ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSa * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The SZIG framework serves as a means to publish internal statistics. It * consists of three main parts: * - collecting data using events * - aggregate that data and represent 'results' in an N-ary tree * - publish this data through a UNIX domain socket interface * * Data collection: * * Primitive data is collected using events. An event is generated (using * a simple function call) when something happens which is interesting * from a statistics standpoint. For example an event is generated when a * thread is started, but a similar event can be generated when a new * entry becomes available in the licensed IPs hash. * * Data aggregation: * * Aggregator functions are generic functions to be applied to events. When * an event occurs all registered functions are called in the order of * registration. This way several different representations of the same * incoming event data are possible. For example we can register two * functions to the START_THREAD event, one of them counts the current * number of threads, the other counts the total number of threads started * so far. * * Each aggregator function receives a result node as argument which * specifies a location where results can be stored. This result node also * contains a GStaticMutex which makes it easy to synchronize acceseses to * the result data. * * Data publishing: * * A simple UNIX domain socket based interface is provided to publish * the result tree. * * Node naming * * Each node in the SZIG tree is named after its position in the tree, * each level is separated by '.', special characters and '.' are escaped * using the URL-like %XX form. * * Name representation * * As the external representation of SZIG node names contain dots, these * dots must be escaped within node names. An unescaped representation is * used (e.g. ZSzigNode->name contains the name in raw form, without * escapes), conversion between escaped/unescaped forms happens when a * node is looked up. * * Locking * * There are two parallel threads accessing the SZIG tree: * 1) the SZIG thread, which processes incoming events from various * parts of Zorp, * 2) the main thread which gives access to the SZIG tree to zorpctl. * * Two different access methods are defined: value changes, in which case * values are changed in the already established tree structure, and * structure changes which involves adding or removing nodes. * * Locking tree structure changes * This only happens in the SZIG thread, and races with the main thread, * if the main thread is querying the same node that the SZIG thread is * changing. Therefore structure changes need to be protected by a * simple mutex, which is locked by the main thread lookup code and the * code involving structure changes in the SZIG thread. * * Locking value changes * As it is not strictly necessary to show an exact value to zorpctl, * value changes does not need to be locked, except when a complex data * structure requires that (e.g. GString) */ #define Z_SZIG_MAX_LINE 4096 #define Z_SZIG_STATS_INTERVAL 5000 /**< interval at which samples will be taken for the average aggregator */ /** * ZSzigEventCallback: * * This structure describes a callback associated with a given event. It * stores a pointer to the function and an opaque pointer to be passed to * this function once it is invoked. **/ typedef struct _ZSzigEventCallback { ZSzigNode *node; ZSzigEventHandler func; gpointer user_data; } ZSzigEventCallback; /** * ZSzigEventDesc: * * This function describes an event. Currently it contains a list of * callbacks to be called when the event occurs. **/ typedef struct _ZSzigEventDesc { GList *callbacks; } ZSzigEventDesc; /** * ZSzigQueueItem: * * The asynchron queue between SZIG and the rest of Zorp contains elements * of this type. **/ typedef struct _ZSzigQueueItem { ZSzigEvent event; ZSzigValue *param; } ZSzigQueueItem; /** * ZSzigConnection: * * Data associated with a connection accepted through the zorpctl socket. **/ typedef struct _ZSzigConnection { guint ref_cnt; ZStream *stream; } ZSzigConnection; /* event descriptors */ static ZSzigEventDesc event_desc[Z_SZIG_MAX + 1]; /* result tree root */ static ZSzigNode *result_tree_root; /* protects tree structure changes (adding/removing nodes, but not value changes) */ static GStaticMutex result_tree_structure_lock = G_STATIC_MUTEX_INIT; static GStaticMutex result_node_gstring_lock = G_STATIC_MUTEX_INIT; /* queue to serialize requests through */ static GAsyncQueue *szig_queue = NULL; /* SZIG values */ /** * z_szig_value_repr: * @v: ZSzigValue instance * @buf: result buffer * @buflen: size of @buf * * This function returns the string representation of a ZSzigValue as it * needs to be presented to the user (e.g. zorpctl). **/ void z_szig_value_repr(ZSzigValue *v, gchar *buf, gsize buflen) { z_enter(); switch (v->type) { case Z_SZIG_TYPE_NOTINIT: g_strlcpy(buf, "None", buflen); break; case Z_SZIG_TYPE_LONG: g_snprintf(buf, buflen, "%ld", v->u.long_value); break; case Z_SZIG_TYPE_TIME: g_snprintf(buf, buflen, "%ld:%ld", (glong) v->u.time_value.tv_sec, (glong) v->u.time_value.tv_usec); break; case Z_SZIG_TYPE_STRING: g_static_mutex_lock(&result_node_gstring_lock); if (v->u.string_value) g_strlcpy(buf, v->u.string_value->str, buflen); else g_strlcpy(buf, "", buflen); g_static_mutex_unlock(&result_node_gstring_lock); break; default: g_assert(0); } z_return(); } /** * z_szig_value_copy: * @target: destination ZSzigValue * @source: ZSzigValue to copy * * This function copies the contents of one ZSzigValue to another. * **/ void z_szig_value_copy(ZSzigValue *target, ZSzigValue *source) { z_enter(); if (target->type != Z_SZIG_TYPE_NOTINIT) z_szig_value_free(target, FALSE); target->type = source->type; switch (source->type) { case Z_SZIG_TYPE_NOTINIT: break; case Z_SZIG_TYPE_LONG: target->u.long_value = source->u.long_value; break; case Z_SZIG_TYPE_TIME: memcpy(&target->u.time_value, &source->u.time_value, sizeof(source->u.time_value)); break; case Z_SZIG_TYPE_STRING: target->u.string_value = g_string_new(source->u.string_value->str); break; default: /* copying this type is not supported */ g_assert_not_reached(); } z_return(); } /** * z_szig_value_as_long: * @v: ZSzigValue instance * * This inline function asserts that v is of type Z_SZIG_TYPE_LONG, and * returns the associated value. **/ static inline glong z_szig_value_as_long(ZSzigValue *v) { g_assert(v->type == Z_SZIG_TYPE_LONG); return v->u.long_value; } /** * z_szig_value_as_time: * @v: ZSzigValue instance * * This inline function asserts that v is of type Z_SZIG_TYPE_TIME, and * returns the associated value. **/ static inline const GTimeVal * z_szig_value_as_time(ZSzigValue *v) { g_assert(v->type == Z_SZIG_TYPE_TIME); return &v->u.time_value; } /** * z_szig_value_as_string: * @v: ZSzigValue instance * * This inline function asserts that v is of type Z_SZIG_TYPE_STRING, and * returns the associated value. **/ static inline const gchar * z_szig_value_as_string(ZSzigValue *v) { g_assert(v->type == Z_SZIG_TYPE_STRING); return v->u.string_value->str; } /** * z_szig_value_as_gstring: * @v: ZSzigValue instance * * This inline function asserts that v is of type Z_SZIG_TYPE_STRING, and * returns the associated value as a GString pointer. **/ static inline GString * z_szig_value_as_gstring(ZSzigValue *v) { g_assert(v->type == Z_SZIG_TYPE_STRING); return v->u.string_value; } /** * z_szig_value_new_long: * @val: value to store as a ZSzigValue * * ZSzigValue constructor which stores a long in the newly created * ZSzigValue instance. **/ ZSzigValue * z_szig_value_new_long(glong val) { ZSzigValue *v = g_new(ZSzigValue, 1); v->type = Z_SZIG_TYPE_LONG; v->u.long_value = val; return v; } /** * z_szig_value_new_time: * @val: value to store as a ZSzigValue * * ZSzigValue constructor which stores a time in the newly created * ZSzigValue instance. **/ ZSzigValue * z_szig_value_new_time(GTimeVal *val) { ZSzigValue *v = g_new(ZSzigValue, 1); v->type = Z_SZIG_TYPE_TIME; v->u.time_value = *val; return v; } /** * z_szig_value_new_string: * @val: value to store as a ZSzigValue * * ZSzigValue constructor which stores a string in the newly created * ZSzigValue instance. **/ ZSzigValue * z_szig_value_new_string(const gchar *val) { ZSzigValue *v = g_new(ZSzigValue, 1); v->type = Z_SZIG_TYPE_STRING; v->u.string_value = g_string_new(val); return v; } /** * z_szig_value_add_connection_prop: * @v: ZSzigValue instance * @name: property name * @value: property value * * This function adds a new property to an already created ZSzigValue * instance. **/ void z_szig_value_add_connection_prop(ZSzigValue *v, const gchar *name, const gchar *value) { z_enter(); g_assert(v->type == Z_SZIG_TYPE_CONNECTION_PROPS); if (v->u.service_props.string_count == Z_SZIG_MAX_PROPS) { z_log(NULL, CORE_ERROR, 0, "Internal error, error adding service property, service properties are limited to 16 elements; add_name='%s', add_value='%s'", name, value); z_return(); } v->u.service_props.string_list[v->u.service_props.string_count * 2] = g_strdup(name); v->u.service_props.string_list[v->u.service_props.string_count * 2 + 1] = g_strdup(value); v->u.service_props.string_count++; z_return(); } /** * z_szig_value_new_connection_props_va: * @service: service name * @instance_id: instance id (e.g. sequence number of this service) * @sec_conn_id: secondary connection id * @related_id: related connection id * @name: first property name * * ZSzigValue constructor which stores service parameters and associatd * property list in the newly created ZSzigValue instance. The list of * properties is passed as a va_list. **/ ZSzigValue * z_szig_value_new_connection_props_va(const gchar *service, gint instance_id, gushort sec_conn_id, gushort related_id, const gchar *name, va_list l) { ZSzigValue *v = g_new0(ZSzigValue, 1); z_enter(); v->type = Z_SZIG_TYPE_CONNECTION_PROPS; v->u.service_props.name = g_strdup(service); v->u.service_props.instance_id = instance_id; v->u.service_props.sec_conn_id = sec_conn_id; v->u.service_props.related_id = related_id; while (name) { z_szig_value_add_connection_prop(v, name, va_arg(l, gchar *)); name = va_arg(l, gchar *); } z_return(v); } /** * z_szig_value_new_connection_props: * @service: service name * @instance_id: instance id (e.g. sequence number of this service) * @sec_conn_id: secondary connection id * @related_id: related connection id * @name: first property name * * ZSzigValue constructor which stores service parameters and associated * property list in the newly created ZSzigValue instance. The * properties are passed as variable arguments. **/ ZSzigValue * z_szig_value_new_connection_props(const gchar *service, gint instance_id, gushort sec_conn_id, gushort related_id, const gchar *name, ...) { ZSzigValue *v; va_list l; z_enter(); va_start(l, name); v = z_szig_value_new_connection_props_va(service, instance_id, sec_conn_id, related_id, name, l); va_end(l); z_return(v); } /** * z_szig_value_add_prop: * @v: ZSzigValue instance * @name: property name * @value: property value * * This function adds a new property to an already created ZSzigValue * instance. **/ void z_szig_value_add_prop(ZSzigValue *v, const gchar *name, ZSzigValue *value) { z_enter(); g_assert(v->type == Z_SZIG_TYPE_PROPS); if (v->u.service_props.string_count == Z_SZIG_MAX_PROPS) { z_log(NULL, CORE_ERROR, 0, "Internal error, error adding property, properties are limited to 16 elements; add_name='%s'", name); z_return(); } v->u.props_value.name_list[v->u.props_value.value_count] = g_strdup(name); v->u.props_value.value_list[v->u.props_value.value_count] = value; v->u.props_value.value_count++; z_return(); } /** * z_szig_value_new_props_va: * @service: service name * @name: first property name * * ZSzigValue constructor which stores service parameters and associatd * property list in the newly created ZSzigValue instance. The list of * properties is passed as a va_list. **/ ZSzigValue * z_szig_value_new_props_va(const gchar *name, const gchar *first_prop, va_list l) { ZSzigValue *v = g_new0(ZSzigValue, 1); const gchar *prop; z_enter(); v->type = Z_SZIG_TYPE_PROPS; v->u.props_value.name = g_strdup(name); prop = first_prop; while (prop) { z_szig_value_add_prop(v, prop, va_arg(l, ZSzigValue *)); prop = va_arg(l, gchar *); } z_return(v); } /** * z_szig_value_new_props: * @name: node name * @name: first property name * * ZSzigValue constructor which stores a named * property list in the newly created ZSzigValue instance. The * properties are passed as variable arguments. **/ ZSzigValue * z_szig_value_new_props(const gchar *name, const gchar *first_prop, ...) { ZSzigValue *v; va_list l; z_enter(); va_start(l, first_prop); v = z_szig_value_new_props_va(name, first_prop, l); va_end(l); z_return(v); } /** * z_szig_value_free: * @v: ZSzigValue instance * * This function frees a ZSzigValue instance. **/ void z_szig_value_free(ZSzigValue *v, gboolean free_inst) { gint i; gint type; z_enter(); if (v) { type = v->type; v->type = Z_SZIG_TYPE_NOTINIT; switch (type) { case Z_SZIG_TYPE_STRING: g_string_free(v->u.string_value, TRUE); break; case Z_SZIG_TYPE_CONNECTION_PROPS: for (i = 0; i < v->u.service_props.string_count * 2; i++) g_free(v->u.service_props.string_list[i]); g_free(v->u.service_props.name); break; case Z_SZIG_TYPE_PROPS: for (i = 0; i < v->u.props_value.value_count; i++) { g_free(v->u.props_value.name_list[i]); z_szig_value_free(v->u.props_value.value_list[i], TRUE); } g_free(v->u.props_value.name); break; } if (free_inst) g_free(v); } z_return(); } /** * z_szig_node_new: * @name: unescaped name of the SZIG node to create * * This function creates a new z_szig_node data structure with data * initialized to zero and name initialized to the @name argument. A * ZSzigNode is a node in the SZIG tree, each has an associated name and * optionally an associated "data" which can be used by aggregators to store * their state (for example the average aggregator stores its state here) * * Returns: the newly allocated ZSzigNode structure **/ static ZSzigNode * z_szig_node_new(const gchar *name) { ZSzigNode *n = g_new0(ZSzigNode, 1); z_enter(); n->name = g_strdup(name); n->children = g_ptr_array_new(); z_return(n); } /** * z_szig_node_set_data: * @node: ZSzigNode instance * @agr_data: state needed by an aggregator * @notify: GDestroyNotify function to free @agr_data * * This function sets the data associated with node. **/ static inline void z_szig_node_set_data(ZSzigNode *node, gpointer agr_data, GDestroyNotify notify) { z_enter(); node->agr_data = agr_data; node->agr_notify = notify; z_return(); } /** * z_szig_node_get_data: * @node: ZSzigNode instance * * This function returns the data associated with node. **/ static inline gpointer z_szig_node_get_data(ZSzigNode *node) { return node->agr_data; } /** * z_szig_node_free: * @n: SZIG node to free * * This function frees a SZIG node from the result tree. It is currently not * used as nodes are never removed from the tree. **/ static void z_szig_node_free(ZSzigNode *n) { guint i; z_enter(); if (n->name) g_free(n->name); if (n->agr_notify) n->agr_notify(n->agr_data); z_szig_value_free(&n->value, FALSE); for (i = 0; i < n->children->len; i++) z_szig_node_free((ZSzigNode *) n->children->pdata[i]); g_ptr_array_free(n->children, TRUE); g_free(n); z_return(); } /** * z_szig_node_lookup_child: * @root: lookup in this node * @name: unescaped name to look up * @ndx: index where the child should have been found * * This function searches a child node by its name. **/ static ZSzigNode * z_szig_node_lookup_child(ZSzigNode *root, const gchar *name, gint *ndx) { gint l, h, m = 0, cmp; ZSzigNode *n; z_enter(); if (!root) z_return(NULL); l = 0; h = root->children->len - 1; while (l <= h) { m = (l + h) >> 1; n = g_ptr_array_index(root->children, m); cmp = strcmp(n->name, name); if (cmp > 0) { h = m - 1; } else if (cmp < 0) { l = m + 1; } else { if (ndx) *ndx = m; z_return(n); } } if (ndx) *ndx = l; z_return(NULL); } /** * z_szig_node_insert_child: * @root: insert a child to this node * @insert_point: insert the child at this position * @child: insert this child * * This function adds a child node. **/ static gboolean z_szig_node_insert_child(ZSzigNode *root, gint insert_point, ZSzigNode *child) { z_enter(); if (insert_point == -1) { if (z_szig_node_lookup_child(root, child->name, &insert_point)) z_return(FALSE); /* already present? */ } g_ptr_array_set_size(root->children, root->children->len + 1); memmove(&root->children->pdata[insert_point+1], &root->children->pdata[insert_point], (root->children->len - insert_point - 1) * sizeof(gpointer)); root->children->pdata[insert_point] = child; z_return(TRUE); } /** * z_szig_node_remove_child: * @root: remove a child from this node * @remove_point: remove the child from this position * * This function removes and frees a child node. **/ static void z_szig_node_remove_child(ZSzigNode *root, gint remove_point) { ZSzigNode *child; z_enter(); g_assert((guint) remove_point < root->children->len); child = root->children->pdata[remove_point]; memmove(&root->children->pdata[remove_point], &root->children->pdata[remove_point+1], (root->children->len - remove_point - 1) * sizeof(gpointer)); g_ptr_array_set_size(root->children, root->children->len - 1); z_szig_node_free(child); z_return(); } /** * z_szig_node_add_named_child: * @root: add a child to this node * @name: unescaped name of the new child * * This function inserts a new child node with the name specified in the * arguments. **/ static ZSzigNode * z_szig_node_add_named_child(ZSzigNode *root, const gchar *name) { gint ndx; ZSzigNode *child; z_enter(); child = z_szig_node_lookup_child(root, name, &ndx); if (!child) { child = z_szig_node_new(name); z_szig_node_insert_child(root, ndx, child); } g_assert(child); z_return(child); } /* * SZIG Tree */ /** * z_szig_xdigit_value: * @x: hexadecimal character * * This function returns the value for a nibble. **/ static inline gint z_szig_xdigit_value(guchar x) { x = toupper(x); if (x >= '0' && x <= '9') return x - '0'; else if (x >= 'A' && x <= 'F') return x - 'A' + 10; return 0; } /** * z_szig_unescape_name: * @name: name to remove escaping from * @buf: result buffer * * This function converts a name from external to internal representation by * resolving escaped characters. **/ static gchar * z_szig_unescape_name(const gchar *name, gchar **buf) { const guchar *src; GString *dst; dst = g_string_sized_new(32); for (src = (guchar *)name; *src; src++) { if (*src == '%') { if (isxdigit(*(src+1)) && isxdigit(*(src+2))) g_string_append_c(dst, (z_szig_xdigit_value(*(src+1)) << 4) | z_szig_xdigit_value(*(src+2))); src += 2; } else { g_string_append_c(dst, *src); } } *buf = dst->str; return g_string_free(dst, FALSE); } /** * z_szig_escape_name: * @name: name to add escaping to * @buf: result buffer * @buflen: size of @buf * * This function converts a name from internal to external representation by * escaping various characters. **/ static gchar * z_szig_escape_name(const gchar *name, gchar **buf) { const guchar *src; GString *dst; dst = g_string_sized_new(32); for (src = (guchar *)name; *src; src++) { if (*src <= 32 || *src == '.' || *src == '%' || *src > 0x7f) { g_string_append_printf(dst, "%%%02X", *src); } else { g_string_append_c(dst, *src); } } *buf = dst->str; return g_string_free(dst, FALSE); } /** * z_szig_tree_lookup: * @node_name: the escaped path to the variable to look up * @create: specifies whether an empty node should be created if the name is * not found * * This function looks up or creates a node in the result tree. Names are * dot separated paths which specify a location in our N-ary tree. Locking * depends on whether we are in the SZIG or in the main thread: * * * in the SZIG thread no locks need to be acquired to look up nodes as * all possible changes are performed in the SZIG thread itself * * if the @create argument is TRUE, even the SZIG thread needs to * acquire result_tree_structure_lock prior to calling this function * * in the main thread callers always need to acquire * result_tree_structure_lock even for looking up elements * * Returns: the SZIG node if found and NULL otherwise **/ ZSzigNode * z_szig_tree_lookup(const gchar *node_name, gboolean create, ZSzigNode **parent, gint *parent_ndx) { gchar **split; ZSzigNode *root, *node = NULL; gint i; z_enter(); if (strlen(node_name) == 0) { /* root node */ *parent = NULL; *parent_ndx = -1; z_return(result_tree_root); } split = g_strsplit(node_name, ".", 0); if (!split) z_return(NULL); node = root = result_tree_root; for (i = 0; node && split[i]; i++) { gint insert_point = -1; gchar *unescaped_name; z_szig_unescape_name(split[i], &unescaped_name); node = z_szig_node_lookup_child(root, unescaped_name, &insert_point); if (parent) *parent = root; if (parent_ndx) *parent_ndx = insert_point; if (!node && create) { /* NOTE: tree structure changes should be locked by * result_tree_structure_lock, however this create * functionality is only used during initialization where * locking is not needed */ node = z_szig_node_new(unescaped_name); z_szig_node_insert_child(root, insert_point, node); } g_free(unescaped_name); root = node; } if (!node) { if (parent) *parent = NULL; if (parent_ndx) *parent_ndx = -1; } g_strfreev(split); z_return(node); } /** * z_szig_agr_store: * @node: result node * @ev: event, not used * @p: event parameter, stored in destination node * @user_data: not used * * This aggregator function simply stores its argument in the SZIG tree. **/ void z_szig_agr_store(ZSzigNode *node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p, gpointer user_data G_GNUC_UNUSED) { z_enter(); z_szig_value_copy(&node->value, p); z_return(); } /** * z_szig_agr_count_inc: * @node: result node * @ev: event, not used * @p: event parameter, not used * @user_data: not used * * This aggregator function increments the result value in a thread * synchronized manner. **/ void z_szig_agr_count_inc(ZSzigNode *node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { z_enter(); node->value.type = Z_SZIG_TYPE_LONG; node->value.u.long_value++; z_return(); } /** * z_szig_agr_count_dec: * @node: result node * @ev: event, not used * @p: event parameter, not used * @user_data: not used * * This aggregator function decrements the result value in a thread * synchronized manner. **/ void z_szig_agr_count_dec(ZSzigNode *node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { z_enter(); node->value.type = Z_SZIG_TYPE_LONG; node->value.u.long_value--; z_return(); } /** * This aggregator function stores the maximum of the values. * * @param[in, out] target_node result node * @param ev event, not used * @param p event parameter, not used * @param[in] user_data source node name * * This aggregator function should be called for every change of the source value. It will * track the highest value seen over its invocations. * * @note the maximum starts from 0, so it won't work with negative values. **/ static void z_szig_agr_maximum(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p G_GNUC_UNUSED, gpointer user_data) { const gchar * const source_node_name = (const gchar * const) user_data; ZSzigNode *source_node; glong current_max, value; z_enter(); source_node = z_szig_tree_lookup(source_node_name, FALSE, NULL, NULL); if (!source_node) { /*LOG This message indicates an internal error, please contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 3, "Invalid maximum aggregator, no source node; source_node='%s'", source_node_name); z_return(); } if (target_node->value.type != Z_SZIG_TYPE_LONG) { target_node->value.type = Z_SZIG_TYPE_LONG; current_max = target_node->value.u.long_value = 0; } else current_max = z_szig_value_as_long(&target_node->value); value = z_szig_value_as_long(&source_node->value); if (value > current_max) { target_node->value.type = Z_SZIG_TYPE_LONG; target_node->value.u.long_value = value; } z_leave(); } /** * State for the maximum aggregator function. **/ typedef struct _ZSzigMaxDiffState { glong last_value; ZSzigNode *source_node; /**< doesn't own this object */ } ZSzigMaxDiffState; /** * This aggregator function stores the maximal increment seen between its invocations. * * @param[in, out] target_node result node * @param ev event, not used * @param p event parameter, not used * @param[in] user_data source node name * * This aggregator function should be called over regular intervals. It will track the highest * increment of the value of the source node seen between two calls. The source node should be * a sequence number, e.g. incremented for each event. * * @note the maximum starts from 0, so it won't work with negative values. **/ void z_szig_agr_maximum_diff(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p G_GNUC_UNUSED, gpointer user_data) { const gchar *source_node_name = (const gchar *) user_data; ZSzigMaxDiffState *max_state = NULL; glong current_value; glong diff = 0; z_enter(); max_state = (ZSzigMaxDiffState *)z_szig_node_get_data(target_node); if (!max_state) /* initialize state */ { max_state = g_new0(ZSzigMaxDiffState, 1); max_state->source_node = z_szig_tree_lookup(source_node_name, FALSE, NULL, NULL); z_szig_node_set_data(target_node, max_state, g_free); } if (!max_state->source_node) { /*LOG This message indicates an internal error, please contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 3, "Invalid maximum aggregator, no source node; source_node='%s'", source_node_name); z_return(); } current_value = z_szig_value_as_long(&max_state->source_node->value); diff = (current_value - max_state->last_value) * 1000 / Z_SZIG_STATS_INTERVAL; max_state->last_value = current_value; if ((target_node->value.type != Z_SZIG_TYPE_LONG) || (target_node->value.u.long_value < diff)) { target_node->value.type = Z_SZIG_TYPE_LONG; target_node->value.u.long_value = diff; } z_return(); } /** * State for the average aggregator function. **/ typedef struct _ZSzigAvgState { glong last_value; ZSzigNode *source_node; GQueue *values; /**< values received in the last interval */ glong sum; /**< sum of values */ glong interval; /**< interval to average over, in seconds */ } ZSzigAvgState; /** * Type of records to store in ZSzigAvgState::values. **/ typedef struct _ZSzigAvgStateValue { glong value; /**< data point */ GTimeVal value_time; /**< time at which it was collected */ } ZSzigAvgStateValue; /** * Free the ZSzigAvgState structure, including the queue. * * @todo FIXME: check function name against naming conventions **/ static inline void z_szig_agr_average_free(gpointer data) { ZSzigAvgState *self = (ZSzigAvgState *)data; g_queue_free(self->values); g_free(data); } /** * Check if check_time is earlier (older) than (end_time - interval). * * @param[in] check_time time value to check * @param[in] end_time end of interval the beginning of which is checked * @param[in] interval length of interval in seconds * * @returns TRUE if check_time is earlier than the interval * * @todo FIXME: check function name against naming conventions **/ static inline gboolean z_szig_agr_average_is_older(GTimeVal check_time, GTimeVal end_time, glong interval) { g_time_val_add(&end_time, -interval*1000000UL); /* was passed by value -- end_time becomes beginning_time */ if (check_time.tv_sec < end_time.tv_sec) return TRUE; if (check_time.tv_sec > end_time.tv_sec) return FALSE; if (check_time.tv_usec < end_time.tv_usec) return TRUE; return FALSE; } /** * Aggregator function to calculate the average value for a node over a specific * time interval. * * @param[in, out] target_node result node * @param ev event, not used * @param[in] p event parameter, assumed to be a time * @param[in] user_data source node name * * This aggregator function should be registered to an event with TIME * parameter, it calculates the average value for a node over a time interval * that is currently specified by the name of the node: the last numeric characters * of the name should be 1, 5 or 15 for 1 minute, 5 minutes and 15 minutes respectively. * The source node should be a sequence number, e.g. incremented for each event. * The increments will be averaged. **/ static void z_szig_agr_average_rate(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p, gpointer user_data) { const gchar *source_node_name = (const gchar *) user_data; ZSzigAvgState *avg_state; const GTimeVal *current_time; glong current_value; ZSzigAvgStateValue *oldest; ZSzigAvgStateValue *new_value; glong diff = 0; z_enter(); target_node->value.type = Z_SZIG_TYPE_LONG; avg_state = (ZSzigAvgState *) z_szig_node_get_data(target_node); if (!avg_state) /* initialize state */ { char *last_char; avg_state = g_new0(ZSzigAvgState, 1); avg_state->values = g_queue_new(); /* parse the interval from the node name (which ends with avg1, avg5 or avg15) */ /** @todo FIXME: this is a really ugly solution, better find a better one. */ last_char = strchr(target_node->name, '\0') - 1; if (*last_char == '1') /* avg1 */ { avg_state->interval = 1 * 60; } else if (*last_char == '5') /* avg5 or avg15 */ { last_char--; if (*last_char == '1') /* avg15 */ { avg_state->interval = 15 * 60; } else /* avg5 */ { avg_state->interval = 5 * 60; } } else { /*LOG This message indicates an internal error, please contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 3, "Failed to parse interval from node name; target_node.name='%s'", target_node->name); /** @todo FIXME: discuss log message format */ g_assert_not_reached(); } z_szig_node_set_data(target_node, avg_state, z_szig_agr_average_free); } if (!avg_state->source_node) avg_state->source_node = z_szig_tree_lookup(source_node_name, FALSE, NULL, NULL); if (!avg_state->source_node) { /*LOG This message indicates an internal error, please contact your Zorp support for assistance. */ z_log(NULL, CORE_ERROR, 3, "Invalid average aggregator, no source node; source_node='%s'", source_node_name); z_return(); } current_time = z_szig_value_as_time(p); current_value = z_szig_value_as_long(&avg_state->source_node->value); diff = current_value - avg_state->last_value; avg_state->last_value = current_value; /* while first value in the queue (peek) exists and is too old */ oldest = g_queue_peek_head(avg_state->values); while (oldest && z_szig_agr_average_is_older(oldest->value_time, *current_time, avg_state->interval)) { /* subtract it from sum and pop it off the queue */ avg_state->sum -= oldest->value; g_free(g_queue_pop_head(avg_state->values)); /* get the next one */ oldest = g_queue_peek_head(avg_state->values); } /* if the queue is empty, set the sum to 0 (just to be sure) */ if (g_queue_is_empty(avg_state->values)) avg_state->sum = 0; /* put (diff, current_time) into the queue if diff isn't 0 (0-s don't need to be tracked) */ if (diff != 0) { new_value = g_new0(ZSzigAvgStateValue, 1); new_value->value = diff; new_value->value_time = *current_time; g_queue_push_tail(avg_state->values, new_value); avg_state->sum += diff; } /* calculate average using sum and interval requested */ target_node->value.type = Z_SZIG_TYPE_LONG; target_node->value.u.long_value = avg_state->sum / avg_state->interval; z_return(); } /** * z_szig_agr_append_string: * @target_node: result node * @ev: event, not used * @p: event parameter, should be a string * @user_data: not used * * This aggregator function appends its parameter to the value of the target * node. **/ void z_szig_agr_append_string(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p, gpointer user_data G_GNUC_UNUSED) { z_enter(); if (target_node->value.type == Z_SZIG_TYPE_NOTINIT) { target_node->value.type = Z_SZIG_TYPE_STRING; target_node->value.u.string_value = g_string_new(z_szig_value_as_string(p)); } else { g_static_mutex_lock(&result_node_gstring_lock); g_string_sprintfa(z_szig_value_as_gstring(&target_node->value), ":%s", z_szig_value_as_string(p)); g_static_mutex_unlock(&result_node_gstring_lock); } z_return(); } /** * Internal state to preserve between calls to z_szig_agr_per_zone_count_print_entry. **/ typedef struct _ZSzigAgrCountPrintState { GString *printout; gboolean first; } ZSzigAgrCountPrintState; /** * Convert an entry in the hash table used by z_szig_agr_per_zonepair_count into a string and print it into the state. * * @param[in] z zone which must be a char * * @param[in] v value which must be a gulong * @param[in, out] s state which must be a ZSzigArgCountPrintState and start with an empty printout and first=TRUE * * Intended for use with g_hash_table_foreach. **/ static void z_szig_agr_per_zone_count_print_entry(gpointer z, gpointer v, gpointer s) { ZSzigAgrCountPrintState *state = (ZSzigAgrCountPrintState *)s; char *zone = (char *)z; gulong *value = v; if (state->first) state->first = FALSE; else g_string_append(state->printout, ", "); g_string_append_printf(state->printout, "%s(%ld)", zone, *value); } /** * GDestroyNotify-compatible function to free a GHashTable (for when we don't want to care about reference counting it). * * @param[in] data pointer to GHashTable **/ static void z_hash_table_free(gpointer data) { GHashTable *hashtable = (GHashTable *)data; g_hash_table_destroy(hashtable); /* free keys and values */ g_free(data); /* free the table itself */ } /** * Update per-zonepair connection count for the service. * * @param[in, out] service szig node of service * @param[in, out] related szig node of current connection * * @note refactor things done for each side into separate function(s)? **/ void z_szig_agr_per_zone_count(ZSzigNode *service, ZSzigNode *related) { ZSzigNode *server_zone_node; ZSzigNode *client_zone_node; char *inbound_zone_name = NULL; char *outbound_zone_name = NULL; ZSzigNode *inbound_zones_node; ZSzigNode *outbound_zones_node; GHashTable *inbound_hash; GHashTable *outbound_hash; gulong *inbound_counter; gulong *outbound_counter; GString *inbound_stats_string; GString *outbound_stats_string; ZSzigAgrCountPrintState inbound_print_state; ZSzigAgrCountPrintState outbound_print_state; /* check if we know both the client zone and the server zone */ client_zone_node = z_szig_node_lookup_child(related, "client_zone", NULL); if (!client_zone_node) return; if (client_zone_node->value.type != Z_SZIG_TYPE_STRING) return; server_zone_node = z_szig_node_lookup_child(related, "server_zone", NULL); if (!server_zone_node) return; if (server_zone_node->value.type != Z_SZIG_TYPE_STRING) return; /* get keys for the hash tables */ inbound_zone_name = server_zone_node->value.u.string_value->str; outbound_zone_name = client_zone_node->value.u.string_value->str; /* find or create stat nodes */ g_static_mutex_lock(&result_tree_structure_lock); inbound_zones_node = z_szig_node_add_named_child(service, "inbound_zones"); inbound_zones_node->value.type = Z_SZIG_TYPE_STRING; outbound_zones_node = z_szig_node_add_named_child(service, "outbound_zones"); outbound_zones_node->value.type = Z_SZIG_TYPE_STRING; g_static_mutex_unlock(&result_tree_structure_lock); /* get or create hash tables (state) */ inbound_hash = (GHashTable *)z_szig_node_get_data(inbound_zones_node); if (!inbound_hash) { inbound_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); z_szig_node_set_data(inbound_zones_node, inbound_hash, z_hash_table_free); } outbound_hash = (GHashTable *)z_szig_node_get_data(outbound_zones_node); if (!outbound_hash) { outbound_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); z_szig_node_set_data(outbound_zones_node, outbound_hash, z_hash_table_free); } /* lookup or create entry in hash tables */ inbound_counter = g_hash_table_lookup(inbound_hash, inbound_zone_name); if (!inbound_counter) { inbound_counter = g_new0(gulong, 1); g_hash_table_insert(inbound_hash, g_strdup(inbound_zone_name), inbound_counter); } outbound_counter = g_hash_table_lookup(outbound_hash, outbound_zone_name); if (!outbound_counter) { outbound_counter = g_new0(gulong, 1); g_hash_table_insert(outbound_hash, g_strdup(outbound_zone_name), outbound_counter); } /* increment counters */ (*inbound_counter)++; (*outbound_counter)++; /* update stats strings (node values) */ inbound_stats_string = g_string_sized_new(32); /* perhaps worth finetuning */ inbound_print_state.printout = inbound_stats_string; inbound_print_state.first = TRUE; g_hash_table_foreach(inbound_hash, z_szig_agr_per_zone_count_print_entry, &inbound_print_state); g_static_mutex_lock(&result_node_gstring_lock); if (inbound_zones_node->value.u.string_value) g_string_free(inbound_zones_node->value.u.string_value, TRUE); inbound_zones_node->value.u.string_value = inbound_print_state.printout; g_static_mutex_unlock(&result_node_gstring_lock); outbound_stats_string = g_string_sized_new(32); /* perhaps worth finetuning */ outbound_print_state.printout = outbound_stats_string; outbound_print_state.first = TRUE; g_hash_table_foreach(outbound_hash, z_szig_agr_per_zone_count_print_entry, &outbound_print_state); g_static_mutex_lock(&result_node_gstring_lock); if (outbound_zones_node->value.u.string_value) g_string_free(outbound_zones_node->value.u.string_value, TRUE); outbound_zones_node->value.u.string_value = outbound_print_state.printout; g_static_mutex_unlock(&result_node_gstring_lock); } /** * z_szig_agr_flat_connection_props: * @target_node: result node * @ev: event, not used * @p: event parameter, should be a service_props value * @user_data: not used * * This aggregator function lays out a service property parameter as nodes * under target_node. It is used to represent service information. **/ void z_szig_agr_flat_connection_props(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p, gpointer user_data G_GNUC_UNUSED) { ZSzigServiceProps *props; ZSzigNode *service, *instance, *sec_conn, *related, *node; gchar buf[16]; gint i; z_enter(); g_return_if_fail(p->type == Z_SZIG_TYPE_CONNECTION_PROPS); props = &p->u.service_props; /* create service node */ g_static_mutex_lock(&result_tree_structure_lock); service = z_szig_node_add_named_child(target_node, props->name); g_snprintf(buf, sizeof(buf), "%d", props->instance_id); instance = z_szig_node_add_named_child(service, buf); g_snprintf(buf, sizeof(buf), "%d", props->sec_conn_id); sec_conn = z_szig_node_add_named_child(instance, buf); g_snprintf(buf, sizeof(buf), "%d", props->related_id); related = z_szig_node_add_named_child(sec_conn, buf); for (i = 0; i < props->string_count; i++) { node = z_szig_node_add_named_child(related, props->string_list[i * 2]); if (node->value.type != Z_SZIG_TYPE_NOTINIT) z_szig_value_free(&node->value, FALSE); node->value.type = Z_SZIG_TYPE_STRING; node->value.u.string_value = g_string_new(props->string_list[i * 2 + 1]); } g_static_mutex_unlock(&result_tree_structure_lock); /* called here so that up-to-date data is already available in the tree */ z_szig_agr_per_zone_count(service, related); z_return(); } /** * z_szig_agr_del_connection_props: * @target_node: result node * @ev: event, not used * @p: event parameter, should be a service_props value * @user_data: not used * * This aggregator function removes the service specific children from * target_node. **/ void z_szig_agr_del_connection_props(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p, gpointer user_data G_GNUC_UNUSED) { ZSzigServiceProps *props; ZSzigNode *service, *instance; gchar buf[16]; gint ndx; z_enter(); g_return_if_fail(p->type == Z_SZIG_TYPE_CONNECTION_PROPS); props = &p->u.service_props; service = z_szig_node_lookup_child(target_node, props->name, NULL); g_snprintf(buf, sizeof(buf), "%d", props->instance_id); instance = z_szig_node_lookup_child(service, buf, &ndx); if (!instance) { z_log(NULL, CORE_ERROR, 0, "Internal error, end-of-service notification referred to a non-existent service; service='%s:%d'", props->name, props->instance_id); z_return(); } g_static_mutex_lock(&result_tree_structure_lock); z_szig_node_remove_child(service, ndx); g_static_mutex_unlock(&result_tree_structure_lock); z_return(); } /** * z_szig_agr_flat_props: * @target_node: result node * @ev: event, not used * @p: event parameter, should be a service_props value * @user_data: not used * * This aggregator function lays out a property list parameter as nodes * under target_node. It is used to represent a generic subtree of information. * * FIXME: this function should support recursive Z_SZIG_TYPE_PROPS * structures and flat those out as a recursive tree structure, but it * assumes that any Z_SZIG_TYPE_PROPS value has only a single level of * children. But this is enough for current uses (e.g. RELOAD) **/ void z_szig_agr_flat_props(ZSzigNode *target_node, ZSzigEvent ev G_GNUC_UNUSED, ZSzigValue *p, gpointer user_data G_GNUC_UNUSED) { ZSzigProps *props; ZSzigNode *root, *node; gint i; z_enter(); g_return_if_fail(p->type == Z_SZIG_TYPE_PROPS); props = &p->u.props_value; /* create service node */ g_static_mutex_lock(&result_tree_structure_lock); root = z_szig_node_add_named_child(target_node, props->name); for (i = 0; i < props->value_count; i++) { node = z_szig_node_add_named_child(root, props->name_list[i]); z_szig_value_copy(&node->value, props->value_list[i]); } g_static_mutex_unlock(&result_tree_structure_lock); z_return(); } /** * This function aggregates the results of z_szig_agr_maximum_diff for all services. **/ void z_szig_agr_service_maximum_diff(ZSzigNode *target_node, ZSzigEvent ev, ZSzigValue *p, gpointer user_data) { guint i; z_enter(); for (i = 0; i < target_node->children->len; i++) { ZSzigNode *current_node = (ZSzigNode *) target_node->children->pdata[i]; ZSzigNode *max_node; gchar *escaped_name = NULL; gchar *max_nodename; gchar *src_nodename; escaped_name = z_szig_escape_name(current_node->name, &escaped_name); max_nodename = g_strconcat("service.", escaped_name, ".", (gchar *)user_data, NULL); src_nodename = g_strconcat("service.", escaped_name, ".session_number", NULL); g_free(escaped_name); max_node = z_szig_tree_lookup(max_nodename, TRUE, NULL, NULL); z_szig_agr_maximum_diff(max_node, ev, p, (gpointer)src_nodename); g_free(src_nodename); g_free(max_nodename); } z_return(); } void z_szig_agr_service_average_rate(ZSzigNode *target_node, ZSzigEvent ev, ZSzigValue *p, gpointer user_data) { guint i; z_enter(); for (i = 0; i < target_node->children->len; i++) { ZSzigNode *current_node = (ZSzigNode *) target_node->children->pdata[i]; ZSzigNode *avg_node; gchar *avg_nodename; gchar *src_nodename; gchar *escaped_name = NULL; escaped_name = z_szig_escape_name(current_node->name, &escaped_name); avg_nodename = g_strconcat("service.", escaped_name, ".", (gchar *)user_data, NULL); src_nodename = g_strconcat("service.", escaped_name, ".session_number", NULL); g_free(escaped_name); avg_node = z_szig_tree_lookup(avg_nodename, TRUE, NULL, NULL); z_szig_agr_average_rate(avg_node, ev, p, (gpointer)src_nodename); g_free(src_nodename); g_free(avg_nodename); } z_return(); } static void z_szig_agr_service_maximum(ZSzigNode *target_node, ZSzigEvent ev, ZSzigValue *p, gpointer user_data G_GNUC_UNUSED) { guint i; z_enter(); for (i = 0; i < target_node->children->len; i++) { const ZSzigNode * const current_node = (const ZSzigNode * const) target_node->children->pdata[i]; ZSzigNode *avg_node; gchar *avg_nodename; gchar *src_nodename; gchar *escaped_name = NULL; escaped_name = z_szig_escape_name(current_node->name, &escaped_name); avg_nodename = g_strconcat("service.", escaped_name, ".sessions_max", NULL); src_nodename = g_strconcat("service.", escaped_name, ".sessions_running", NULL); g_free(escaped_name); avg_node = z_szig_tree_lookup(avg_nodename, TRUE, NULL, NULL); z_szig_agr_maximum(avg_node, ev, p, (gpointer)src_nodename); g_free(src_nodename); g_free(avg_nodename); } z_leave(); } /* general framework */ /** * z_szig_event_get_desc: * @ev: event type * * This function returns the event descriptor for the event @ev. **/ static inline ZSzigEventDesc * z_szig_event_get_desc(ZSzigEvent ev) { g_assert(ev >= 0 && ev <= Z_SZIG_MAX); return &event_desc[ev]; } /** * z_szig_process_event: * @ev: szig event (Z_SZIG_*) * @param: a ZSzigValue parameter for event * * Main SZIG entry point used by various parts of Zorp to inform SZIG about * an interesting event. It can be called from all threads, information is * sent through a GAsyncQueue. **/ void z_szig_event(ZSzigEvent ev, ZSzigValue *param) { ZSzigQueueItem *q = g_new(ZSzigQueueItem, 1); z_enter(); q->event = ev; q->param = param; if (szig_queue) { static gint warn_counter = 1; z_trace(NULL, "Sending szig event; object='%p'", q); if (g_async_queue_length(szig_queue) > 1000 * warn_counter) { z_log(NULL, CORE_ERROR, 1, "Internal error, SZIG queue overflow;"); warn_counter++; } g_async_queue_push(szig_queue, q); } z_return(); } /** * z_szig_process_event: * @ev: szig event to handle * @param: szig event handle * * This function is called from the SZIG thread to process queued events. **/ void z_szig_process_event(ZSzigEvent ev, ZSzigValue *param) { ZSzigEventDesc *d; ZSzigEventCallback *cb; GList *p; z_enter(); d = z_szig_event_get_desc(ev); for (p = d->callbacks; p; p = g_list_next(p)) { cb = (ZSzigEventCallback *) p->data; cb->func(cb->node, ev, param, cb->user_data); } z_szig_value_free(param, TRUE); z_return(); } /** * z_szig_register_handler: * @ev: szig event * @func: event handler function * @node_name: target node name, where the aggregator stores calculated information * @user_data: pointer passed to @func * * This function registers an aggregator for the specified event using the * specified target node. **/ static void z_szig_register_handler(ZSzigEvent ev, ZSzigEventHandler func, const gchar *node_name, gpointer user_data) { ZSzigEventCallback *cb; ZSzigEventDesc *d; d = z_szig_event_get_desc(ev); cb = g_new0(ZSzigEventCallback, 1); cb->node = z_szig_tree_lookup(node_name, TRUE, NULL, NULL); cb->user_data = user_data; cb->func = func; d->callbacks = g_list_append(d->callbacks, cb); } /* basic SZIG events */ /** * z_szig_thread_started: * @self: ZThread instance, not used * @user_data: not used * * This function is registered as a thread startup function. It simply generates a * Z_SZIG_THREAD_START event. **/ static void z_szig_thread_started(ZThread *self G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { z_szig_event(Z_SZIG_THREAD_START, NULL); } /** * z_szig_thread_stopped: * @self: ZThread instance, not used * @user_data: not used * * This function is registered as a thread stop function. It simply generates a * Z_SZIG_THREAD_STOP event. **/ static void z_szig_thread_stopped(ZThread *self G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { z_szig_event(Z_SZIG_THREAD_STOP, NULL); } /** * This function is called every Z_SZIG_STATS_INTERVAL milliseconds to generate a SZIG event. * * @param[in] source GSource instance **/ static gboolean z_szig_tick_callback(GSource *source) { GTimeVal current_time; static guint ticks = 0; g_source_get_current_time(source, ¤t_time); z_szig_event(Z_SZIG_TICK, z_szig_value_new_time(¤t_time)); ticks++; return TRUE; } /** * z_szig_thread: * @st: thread parameter, not used * * This is the SZIG thread main function, it basically waits for and * processes SZIG events sent by z_szig_event(). **/ static gpointer z_szig_thread(gpointer st G_GNUC_UNUSED) { if (!szig_queue) return NULL; while (1) { ZSzigQueueItem *q = (ZSzigQueueItem *) g_async_queue_pop(szig_queue); z_trace(NULL, "Received szig event; object='%p'", q); z_szig_process_event(q->event, q->param); g_free(q); } return NULL; } /* SZIG I/O */ /** * z_szig_connection_unref: * @self: ZSzigConnection instance * * This function decrements the reference count for @self and frees it if it * reaches 0. **/ static void z_szig_connection_unref(ZSzigConnection *self) { if (z_decref(&self->ref_cnt) == 0) { g_free(self); } } static void z_szig_node_print_full_name(gchar *buf, size_t buflen, gchar *prefix, ZSzigNode *node) { gchar *escaped_name = NULL; if (prefix && strlen(prefix) > 0) g_snprintf(buf, buflen, "%s.%s\n", prefix, z_szig_escape_name(node->name, &escaped_name)); else g_snprintf(buf, buflen, "%s\n", z_szig_escape_name(node->name, &escaped_name)); if (escaped_name != NULL) g_free(escaped_name); } /** * z_szig_handle_command: * @conn: ZSzigConnection instance * @cmd_line: command to run * * This is the main processing function for the SZIG command channel. It * runs in the main thread whenever an incoming line is detected by the poll * loop. **/ static gboolean z_szig_handle_command(ZSzigConnection *conn, gchar *cmd_line) { gchar response[16384], *cmd, *name; gint node_ndx; ZSzigNode *node, *node_parent = NULL; gchar **argv; const gchar *logspec; gint direction, value; gboolean new_state; z_enter(); argv = g_strsplit(cmd_line, " ", 0); if (!argv || !argv[0]) { if (argv) g_strfreev(argv); z_return(FALSE); } cmd = argv[0]; g_strlcpy(response, "None\n", sizeof(response)); if (strcmp(cmd, "GETVALUE") == 0 || strcmp(cmd, "GETCHILD") == 0 || strcmp(cmd, "GETSBLNG") == 0) { name = argv[1]; g_static_mutex_lock(&result_tree_structure_lock); node = z_szig_tree_lookup(name, FALSE, &node_parent, &node_ndx); if (strcmp(cmd, "GETVALUE") == 0) { if (node) { z_szig_value_repr(&node->value, response, sizeof(response)-1); strncat(response, "\n", sizeof(response)); } } else if (strcmp(cmd, "GETCHILD") == 0) { if (node && node->children->len) { node = (ZSzigNode *) node->children->pdata[0]; z_szig_node_print_full_name(response, sizeof(response), name, node); } } else if (strcmp(cmd, "GETSBLNG") == 0) { if (node && node_parent && (gint) (node_parent->children->len - 1) > node_ndx) { gchar *e = name + strlen(name) - 1; node = (ZSzigNode *) node_parent->children->pdata[node_ndx+1]; while (e > name && *e != '.') e--; *e = 0; z_szig_node_print_full_name(response, sizeof(response), name, node); } } g_static_mutex_unlock(&result_tree_structure_lock); } else if (strcmp(cmd, "LOGGING") == 0) { g_strlcpy(response, "FAIL\n", sizeof(response)); if (!argv[1]) g_strlcpy(response, "FAIL LOGGING subcommand required", sizeof(response)); else if (strcmp(argv[1], "VINC") == 0 || strcmp(argv[1], "VDEC") == 0 || strcmp(argv[1], "VSET") == 0) { if (argv[1][1] == 'I') direction = 1; else if (argv[1][1] == 'D') direction = -1; else direction = 0; if (argv[2]) value = strtol(argv[2], NULL, 10); else value = 0; if (z_log_change_verbose_level(direction, value, &value)) g_snprintf(response, sizeof(response), "OK %d\n", value); else g_snprintf(response, sizeof(response), "FAIL Error changing verbose level\n"); } else if (strcmp(argv[1], "VGET") == 0) { if (z_log_change_verbose_level(1, 0, &value)) g_snprintf(response, sizeof(response), "OK %d\n", value); else g_snprintf(response, sizeof(response), "FAIL Error querying verbose level\n"); } else if (strcmp(argv[1], "GETSPEC") == 0) { if (z_log_change_logspec(NULL, &logspec)) g_snprintf(response, sizeof(response), "OK %s\n", logspec ? logspec : ""); else g_snprintf(response, sizeof(response), "FAIL Error querying logspec\n"); } else if (strcmp(argv[1], "SETSPEC") == 0) { if (argv[2]) { if (z_log_change_logspec(argv[2], &logspec)) g_snprintf(response, sizeof(response), "OK %s\n", logspec); else g_snprintf(response, sizeof(response), "FAIL Error setting logspec\n"); } else { g_snprintf(response, sizeof(response), "FAIL No logspec specified\n"); } } } else if (strcmp(cmd, "DEADLOCKCHECK") == 0) { g_strlcpy(response, "FAIL\n", sizeof(response)); if (!argv[1]) g_strlcpy(response, "FAIL DEADLOCKCHECK subcommand required", sizeof(response)); else if (strcmp(argv[1], "ENABLE") == 0 || strcmp(argv[1], "DISABLE") == 0) { if (argv[1][0] == 'E') new_state = TRUE; else new_state = FALSE; z_process_set_check_enable(new_state); g_snprintf(response, sizeof(response), "OK\n"); } else if (strcmp(argv[1], "GET") == 0) { g_snprintf(response, sizeof(response), "OK %d\n", z_process_get_check_enable() ? 1 : 0); } } else if (strcmp(cmd, "RELOAD") == 0) { if (!argv[1]) { z_main_loop_initiate_reload(FALSE); g_strlcpy(response, "OK Reload initiated", sizeof(response)); } else if (strcmp(argv[1], "RESULT") == 0) { if (z_main_loop_get_last_reload_result()) g_strlcpy(response, "OK Reload successful", sizeof(response)); else g_strlcpy(response, "FAIL Reload failed", sizeof(response)); } else { g_strlcpy(response, "FAIL Unknown RELOAD subcommand", sizeof(response)); } } else if (strcmp(cmd, "COREDUMP") == 0) { if (z_coredump_create() < 0) g_strlcpy(response, "FAIL Dumping core failed", sizeof(response)); else g_strlcpy(response, "OK Core dump created", sizeof(response)); } else { g_strlcpy(response, "FAIL No such command", sizeof(response)); } g_strfreev(argv); if (z_stream_write_buf(conn->stream, response, strlen(response), TRUE, FALSE) != G_IO_STATUS_NORMAL) z_return(FALSE); z_return(TRUE); } /** * z_szig_read_callback: * @stream: ZStream instance * @cond: condition that triggered this callback * @user_data: ZSzigConnection passed as user_data * * This function is invoked whenever a complete line is available from the * zorpctl client. It basically reads the line and runs the command using * z_szig_handle_command(). **/ static gboolean z_szig_read_callback(ZStream *stream, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { ZSzigConnection *conn = (ZSzigConnection *) user_data; gchar buf[Z_SZIG_MAX_LINE]; gsize buflen = sizeof(buf) - 1; GIOStatus res; ZStream *tmp_stream; z_enter(); res = z_stream_line_get_copy(stream, buf, &buflen, NULL); if (res == G_IO_STATUS_NORMAL) { buf[buflen] = 0; if (z_szig_handle_command(conn, buf)) z_return(TRUE); } else if (res == G_IO_STATUS_AGAIN) { z_return(TRUE); } z_stream_close(stream, NULL); tmp_stream = conn->stream; conn->stream = NULL; z_stream_unref(tmp_stream); z_return(FALSE); } /** * z_szig_accept_callback: * @fd: fd that refers to the connection * @client: socket address * @last_connection: this is the last connection (when accept_one was specified) * @user_data: opaque pointer * * This function is called as soon as a new connection is received, it * basically creates a new ZSzigConnection and registers the stream with the * main loop. **/ static gboolean z_szig_accept_callback(ZStream *fdstream, ZSockAddr *client, ZSockAddr *dest, gpointer user_data G_GNUC_UNUSED) { ZSzigConnection *conn; ZStream *linestream, *bufstream; gchar buf[32]; static gint szig_conn_id = 0; g_snprintf(buf, sizeof(buf), "szig/conn:%d/stream", szig_conn_id); szig_conn_id++; z_stream_set_name(fdstream, buf); z_stream_set_nonblock(fdstream, TRUE); linestream = z_stream_line_new(fdstream, Z_SZIG_MAX_LINE, ZRL_EOL_NL); bufstream = z_stream_buf_new(linestream, 1024, 2048); z_stream_unref(fdstream); z_stream_unref(linestream); conn = g_new0(ZSzigConnection, 1); conn->ref_cnt = 1; conn->stream = bufstream; z_stream_set_callback(conn->stream, G_IO_IN, z_szig_read_callback, conn, (GDestroyNotify) z_szig_connection_unref); z_stream_set_cond(conn->stream, G_IO_IN, TRUE); z_stream_attach_source(conn->stream, g_main_context_default()); z_sockaddr_unref(client); z_sockaddr_unref(dest); return TRUE; } /** * z_szig_init: * @instance_name: the virtual name of this Zorp instance * * This function initializes the SZIG subsystem, creates the UNIX domain * socket and initializes basic aggregations. **/ void z_szig_init(const gchar *instance_name) { ZSockAddr *sockaddr; ZListener *listen; gchar buf[256]; GSource *tick_source; result_tree_root = z_szig_node_new(instance_name); memset(event_desc, 0, sizeof(event_desc)); szig_queue = g_async_queue_new(); z_szig_register_handler(Z_SZIG_CONNECTION_START, z_szig_agr_count_inc, "stats.sessions_running", NULL); z_szig_register_handler(Z_SZIG_CONNECTION_STOP, z_szig_agr_count_dec, "stats.sessions_running", NULL); z_szig_register_handler(Z_SZIG_THREAD_START, z_szig_agr_count_inc, "stats.threads_running", NULL); z_szig_register_handler(Z_SZIG_THREAD_STOP, z_szig_agr_count_dec, "stats.threads_running", NULL); z_szig_register_handler(Z_SZIG_THREAD_START, z_szig_agr_count_inc, "stats.thread_number", NULL); z_szig_register_handler(Z_SZIG_THREAD_START, z_szig_agr_maximum, "stats.threads_max", "stats.threads_running"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_average_rate, "stats.thread_rate_avg1", "stats.thread_number"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_average_rate, "stats.thread_rate_avg5", "stats.thread_number"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_average_rate, "stats.thread_rate_avg15", "stats.thread_number"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_maximum_diff, "stats.thread_rate_max", "stats.thread_number"); z_szig_register_handler(Z_SZIG_CONNECTION_PROPS, z_szig_agr_flat_connection_props, "conns", NULL); z_szig_register_handler(Z_SZIG_CONNECTION_STOP, z_szig_agr_del_connection_props, "conns", NULL); z_szig_register_handler(Z_SZIG_SERVICE_COUNT, z_szig_agr_flat_props, "service", NULL); z_szig_register_handler(Z_SZIG_SERVICE_COUNT, z_szig_agr_service_maximum, "service", NULL); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_service_average_rate, "service", "rate_avg1"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_service_average_rate, "service", "rate_avg5"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_service_average_rate, "service", "rate_avg15"); z_szig_register_handler(Z_SZIG_TICK, z_szig_agr_service_maximum_diff, "service", "rate_max"); z_szig_register_handler(Z_SZIG_AUDIT_START, z_szig_agr_count_inc, "stats.audit_running", NULL); z_szig_register_handler(Z_SZIG_AUDIT_STOP, z_szig_agr_count_dec, "stats.audit_running", NULL); z_szig_register_handler(Z_SZIG_AUDIT_START, z_szig_agr_count_inc, "stats.audit_number", NULL); z_szig_register_handler(Z_SZIG_RELOAD, z_szig_agr_flat_props, "info", NULL); /* we need an offset of 2 to count the number of threads that were started before SZIG init */ z_szig_thread_started(NULL, NULL); z_szig_thread_started(NULL, NULL); z_thread_register_start_callback((GFunc) z_szig_thread_started, NULL); z_thread_register_stop_callback((GFunc) z_szig_thread_stopped, NULL); tick_source = g_timeout_source_new(Z_SZIG_STATS_INTERVAL); g_source_set_callback(tick_source, (GSourceFunc) z_szig_tick_callback, tick_source, NULL); g_source_attach(tick_source, g_main_context_default()); g_snprintf(buf, sizeof(buf), "%s.%s", ZORP_SZIG_SOCKET_NAME, instance_name); sockaddr = z_sockaddr_unix_new(buf); listen = z_stream_listener_new("szig/listen", sockaddr, FALSE, 255, z_szig_accept_callback, NULL); if (listen) { if (!z_listener_start(listen)) { /*LOG This message reports that the SZIG framework was unable to create its socket and thus zorpctl won't be able to access and display internal Zorp information. */ z_log(NULL, CORE_INFO, 4, "Failed to create SZIG socket; name='%s'", buf); } z_listener_unref(listen); } z_sockaddr_unref(sockaddr); z_thread_new("szig/thread", z_szig_thread, NULL); } void z_szig_destroy(void) { /* FIXME: free result tree */ } zorp-3.9.5/lib/timestamp.c000066400000000000000000000023411172670260400154440ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi, Panther * Auditor : * Last audited version: * Notes: * ***************************************************************************/ zorp-3.9.5/lib/tpsocket.c000066400000000000000000000044661172670260400153070ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include static gint z_do_ll_getdestname(gint fd, struct sockaddr *sa, socklen_t *salen, guint32 sock_flags G_GNUC_UNUSED) { return getsockname(fd, sa, salen); } #ifndef IP_FREEBIND #define IP_FREEBIND 15 #endif #ifndef IP_TRANSPARENT #define IP_TRANSPARENT 19 #endif static gint z_do_tp40_bind(gint fd, struct sockaddr *sa, socklen_t salen, guint32 sock_flags) { gint on = 1, res; z_enter(); if (sock_flags & ZSF_TRANSPARENT || sock_flags & ZSF_MARK_TPROXY) { if (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)) < 0) setsockopt(fd, SOL_IP, IP_FREEBIND, &on, sizeof(on)); } res = z_do_ll_bind(fd, sa, salen, sock_flags); z_return(res); } static ZSocketFuncs z_tp40_socket_funcs = { z_do_tp40_bind, z_do_ll_accept, z_do_ll_connect, z_do_ll_listen, z_do_ll_getsockname, z_do_ll_getpeername, z_do_ll_getdestname }; gboolean z_tp_socket_init(void) { socket_funcs = &z_tp40_socket_funcs; return TRUE; } zorp-3.9.5/lib/zasauth.c000066400000000000000000000023271172670260400151240ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : SaSA * Auditor : * Last audited version: * Notes: * ***************************************************************************/ zorp-3.9.5/lib/zorp.c000066400000000000000000000205241172670260400144360ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include GMainLoop *main_loop; gint exit_code = 0; guint32 startup_id; const gchar *instance_name; const gchar *virtual_instance_name; gboolean zorp_process_master_mode = TRUE; /* FIXME: the code in this module was straightly copied from main.c and as * such does not really fit into a library. Some generalization would be * appropriate. */ gboolean usr1_received = 0; gboolean usr2_received = 0; static gboolean term_received = 0; static gboolean hup_received = 0; static gboolean reload_result = FALSE; /** * NOTE: this must either be called from a signal handler (in which case * called_from_sighandler must be TRUE, or from a non-main thread as it tries to * communicate via the main thread using a condition variable. **/ void z_main_loop_initiate_reload(gboolean called_from_sighandler) { hup_received = TRUE; if (!called_from_sighandler) { g_main_context_wakeup(NULL); } } gboolean z_main_loop_get_last_reload_result(void) { return reload_result; } void z_main_loop_initiate_termination(gboolean called_from_sighandler) { term_received = TRUE; if (!called_from_sighandler) g_main_context_wakeup(NULL); } void z_read_global_params(ZPolicy *policy) { z_policy_acquire_main(policy); z_policy_var_parse_str(z_global_getattr("config.blob.temp_directory"), (gchar **) &z_blob_system_default_tmpdir); z_policy_var_parse_int64(z_global_getattr("config.blob.max_disk_usage"), &z_blob_system_default_max_disk_usage); z_policy_var_parse_size(z_global_getattr("config.blob.max_mem_usage"), &z_blob_system_default_max_mem_usage); z_policy_var_parse_size(z_global_getattr("config.blob.lowat"), &z_blob_system_default_lowat); z_policy_var_parse_size(z_global_getattr("config.blob.hiwat"), &z_blob_system_default_hiwat); z_policy_var_parse_size(z_global_getattr("config.blob.noswap_max"), &z_blob_system_default_noswap_max); z_policy_release_main(policy); } gboolean z_load_policy(const gchar *policy_file, gchar const **instance_policy_list, gchar const *virtual_instance_name, gint is_master) { ZPolicy *policy; ZPolicy *old_policy; policy = z_policy_new(policy_file); if (!z_policy_boot(policy) || !z_policy_load(policy)) { /*LOG This message indicates that Zorp was unable to load the policy. It is likely that the policy has any kind of syntactical problem. Check the traceback in the log to find out where the problem occurs. */ z_log(NULL, CORE_ERROR, 0, "Error booting & parsing policy;"); z_policy_deinit(policy, instance_policy_list, virtual_instance_name); z_policy_unref(policy); return FALSE; } old_policy = current_policy; current_policy = policy; if (!z_policy_init(policy, instance_policy_list, virtual_instance_name, is_master)) { /* FIXME: deinit bad new configuration */ current_policy = old_policy; z_policy_deinit(policy, instance_policy_list, virtual_instance_name); z_policy_unref(policy); /*LOG This message indicates that Zorp was unable to initialize the policy. */ z_log(NULL, CORE_ERROR, 0, "Error initializing policy;"); return FALSE; } else if (old_policy != NULL) { /* FIXME: deinit old configuration */ z_policy_deinit(old_policy, instance_policy_list, virtual_instance_name); z_policy_unref(old_policy); } return TRUE; } static void z_generate_policy_load_event(const gchar *policy_file, gboolean reload_result) { struct stat st; time_t policy_stamp; if (reload_result) { if (stat(policy_file, &st) < 0) policy_stamp = (time_t) -1; else policy_stamp = st.st_mtime; z_szig_event(Z_SZIG_RELOAD, z_szig_value_new_props("policy", "file", z_szig_value_new_string(policy_file), "file_stamp", z_szig_value_new_long(policy_stamp), "reload_stamp", z_szig_value_new_long(time(NULL)), NULL)); } } void z_main_loop(const gchar *policy_file, const gchar *instance_name, gchar const **instance_policy_list, gchar const *virtual_instance_name, gboolean is_master) { gint new_verbosity; if (!z_load_policy(policy_file, instance_policy_list, virtual_instance_name, is_master)) { /*LOG This message indicates that the loading of the initial policy failed, because of some policy problem. Check the log to find out where the problem occurs. */ z_log(NULL, CORE_ERROR, 0, "Error loading initial policy, exiting;"); /* hack to let our messages get out */ sleep(1); exit_code = 2; return; } /* signal successful start */ z_process_startup_ok(); /* z_process_startup_ok() closes the inherited stderr, we need to open our * own to see messages written to stderr */ if (z_log_get_use_syslog()) z_log_enable_stderr_redirect(TRUE); if (term_received) z_main_loop_quit(0); z_read_global_params(current_policy); z_blob_system_default_init(); z_generate_policy_load_event(policy_file, TRUE); while (g_main_loop_is_running(main_loop)) { g_main_context_iteration(NULL, TRUE); if (usr1_received) { usr1_received = 0; z_log_change_verbose_level(1, 1, &new_verbosity); z_mem_trace_stats(); } if (usr2_received) { usr2_received = 0; z_log_change_verbose_level(-1, 1, &new_verbosity); } if (hup_received) { /*LOG This message reports that Zorp caught a HUP signal and tries to reload its policy. */ z_log(NULL, CORE_INFO, 0, "Reloading policy; policy_file='%s', instance_name='%s'", policy_file, instance_name); if (!z_load_policy(policy_file, instance_policy_list, virtual_instance_name, is_master)) { /*LOG This message indicates that Zorp was unable to load the new policy, and reverts to the old one. Check the logs to find out where the error occurs in the new policy. */ z_log(NULL, CORE_ERROR, 0, "Error reloading policy, reverting to old;"); reload_result = FALSE; } else { reload_result = TRUE; } hup_received = 0; z_generate_policy_load_event(policy_file, reload_result); } if (term_received) { z_main_loop_quit(0); break; } } z_policy_cleanup(current_policy, instance_policy_list, virtual_instance_name, is_master); z_blob_system_default_destroy(); } /** * z_main_loop_quit: * @rc exit code * * Set the exit code to the specified value and tell glib to exit the main loop */ void z_main_loop_quit(int rc) { z_enter(); exit_code = rc; g_main_quit(main_loop); z_return(); } void z_main_loop_init(void) { main_loop = g_main_loop_new(NULL, TRUE); } void z_main_loop_destroy(void) { if (main_loop) { g_main_loop_unref(main_loop); main_loop = NULL; } } zorp-3.9.5/lib/zorp/000077500000000000000000000000001172670260400142675ustar00rootroot00000000000000zorp-3.9.5/lib/zorp/Makefile.am000066400000000000000000000012711172670260400163240ustar00rootroot00000000000000ZORP_H = zorpconfig.h \ modules.h policy.h proxy.h proxystack.h \ pyproxy.h \ nfdynexpect-kernel.h\ dimhash.h zorp.h \ pysockaddr.h pystream.h \ pycore.h \ pyzasauth.h zasauth.h \ satyr.h pysatyr.h \ authprovider.h \ tpsocket.h \ szig.h \ dispatch.h pydispatch.h attach.h connection.h pyattach.h \ plugsession.h zpython.h \ dgram.h pystruct.h pydict.h audit.h \ ifmonitor.h proxygroup.h pyproxygroup.h \ coredump.h notification.h linebalance.h \ pybalance.h pyaudit.h timestamp.h idsconn.h \ proxyssl.h proxysslhostiface.h proxycommon.h pyx509.h \ bllookup.h pyblacklist.h \ kzorp.h kzorp-kernel.h credentials.h pkgincludedir=$(includedir)/zorp pkginclude_HEADERS = $(ZORP_H) zorp-3.9.5/lib/zorp/attach.h000066400000000000000000000021421172670260400157030ustar00rootroot00000000000000#ifndef ZORP_ATTACH_H_INCLUDED #define ZORP_ATTACH_H_INCLUDED #include #include #include typedef struct _ZAttachTCPParams { } ZAttachTCPParams; typedef struct _ZAttachUDPParams { } ZAttachUDPParams; typedef struct _ZAttachParams { gint timeout; gboolean loose; /**< choose port in the same group if the port can't be bound */ gboolean random; /**< choose port in the same group randomly (securely). if TRUE, loose should be TRUE too. */ gint tos; union { ZAttachTCPParams tcp; ZAttachUDPParams udp; }; } ZAttachParams; typedef struct _ZAttach ZAttach; typedef void (*ZAttachCallbackFunc)(ZConnection *, gpointer user_data); gboolean z_attach_start(ZAttach *self, ZPoll *poll, ZSockAddr **local); gboolean z_attach_start_block(ZAttach *self, ZConnection **conn); void z_attach_cancel(ZAttach *self); ZAttach *z_attach_new(ZProxy *proxy, guint proto, ZSockAddr *local, ZSockAddr *remote, ZAttachParams *params, ZAttachCallbackFunc callback, gpointer user_data, GDestroyNotify destroy_data); void z_attach_free(ZAttach *self); #endif zorp-3.9.5/lib/zorp/audit.h000066400000000000000000000004741172670260400155530ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000-2008 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * ***************************************************************************/ #ifndef ZORP_AUDIT_H_INCLUDED #define ZORP_AUDIT_H_INCLUDED #endif zorp-3.9.5/lib/zorp/authprovider.h000066400000000000000000000031761172670260400171630ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef AUTHORIZATION_H_INCLUDED #define AUTHORIZATION_H_INCLUDED #include #include #define ZAuthProvider ZPolicyObj gboolean z_auth_provider_check_passwd(ZAuthProvider *self, gchar *session_id, gchar *username, gchar *passwd, gchar ***groups, ZProxy *proxy); #endif zorp-3.9.5/lib/zorp/bllookup.h000066400000000000000000000000001172670260400162550ustar00rootroot00000000000000zorp-3.9.5/lib/zorp/connection.h000066400000000000000000000013071172670260400166000ustar00rootroot00000000000000#ifndef ZORP_CONNECTION_H_INCLUDED #define ZORP_CONNECTION_H_INCLUDED #include #include #include enum { ZD_PROTO_AUTO = 0, ZD_PROTO_TCP = 1, ZD_PROTO_UDP = 2, }; typedef struct _ZConnection { guint protocol; ZStream *stream; ZSockAddr *remote; /* the peer's address */ ZSockAddr *local; /* the explicit local address (no wildcard port spec) */ ZSockAddr *dest; /* the original destination of the client */ struct _ZDispatchBind *dispatch_bind; } ZConnection; ZConnection *z_connection_new(void); gchar *z_connection_format(ZConnection *conn, gchar *buf, gint buflen); void z_connection_destroy(ZConnection *conn, gboolean close); #endif zorp-3.9.5/lib/zorp/coredump.h000066400000000000000000000002071172670260400162550ustar00rootroot00000000000000#ifndef ZORP_COREDUMP_H_INCLUDED #define ZORP_COREDUMP_H_INCLUDED int z_coredump_create(void); #endif /* ZORP_COREDUMP_H_INCLUDED */ zorp-3.9.5/lib/zorp/credentials.h000066400000000000000000000043421172670260400167400ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_CREDENTIALS_H_INCLUDED #define ZORP_CREDENTIALS_H_INCLUDED #include /** * Stores a credential item, which is an array of strings. * * I.e. in the case of passwords the credential item contains a single string. * On the other hand a key credential may contain a key type, public and private key. */ typedef struct _ZCredentialItem { gint count; /**< Number of strings in the item */ gchar **credentials; /**< Array of credential strings */ } ZCredentialItem; /** * List of credential items */ typedef struct _ZCredentialList { gint count; /**< Number of credential items */ ZCredentialItem *items; /**< Array of credential items */ } ZCredentialList; ZCredentialList *z_proxy_get_credential_list(ZProxy *self, const gchar *username, const gchar *domain, const gchar *target_host, gushort port, const gchar *method); gchar *z_proxy_get_credential_password(ZProxy *self, const gchar *username, const gchar *domain, const gchar *target_host, gushort port); void z_credential_list_free(ZCredentialList *self); #endif zorp-3.9.5/lib/zorp/dgram.h000066400000000000000000000042101172670260400155270ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_DGRAM_H_INCLUDED #define ZORP_DGRAM_H_INCLUDED #include #include #include #include extern ZClass ZDGramListener__class; gboolean z_dgram_init(void); ZListener * z_dgram_listener_new(const gchar *session_id, ZSockAddr *local, guint32 sock_flags, gint rcvbuf, ZAcceptFunc callback, gpointer user_data); extern ZClass ZDGramConnector__class; static inline ZConnector * z_dgram_connector_new(const gchar *session_id, ZSockAddr *local, ZSockAddr *remote, guint32 sock_flags, ZConnectFunc callback, gpointer user_data, GDestroyNotify destroy_data) { return z_connector_new(Z_CLASS(ZDGramConnector), session_id, SOCK_DGRAM, local, remote, sock_flags, callback, user_data, destroy_data); } #endif zorp-3.9.5/lib/zorp/dimhash.h000066400000000000000000000047421172670260400160640ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_DIMHASH_H_INCLUDED #define ZORP_DIMHASH_H_INCLUDED #include #define DIMHASH_WILDCARD 0x001 #define DIMHASH_CONSUME 0x002 #define DIMHASH_MAX_KEYNUM 5 #define DIMHASH_MAX_KEYSIZE 100 typedef struct _ZDimHashTable { GHashTable *hash; guint keynum; guint minkeynum; guint *flags; } ZDimHashTable; typedef gboolean (*ZDimHashFreeFunc)(void *value); void z_dim_hash_key_free(int num, gchar **key); ZDimHashTable *z_dim_hash_table_new(guint minnum, guint num, ...); void z_dim_hash_table_free(ZDimHashTable *self, ZDimHashFreeFunc func); gpointer z_dim_hash_table_lookup(ZDimHashTable *self, guint num, gchar **keys); void z_dim_hash_table_delete(ZDimHashTable *self, guint num, gchar **keys, ZDimHashFreeFunc func); void z_dim_hash_table_insert(ZDimHashTable *self, gpointer value, guint num, gchar **keys); gpointer z_dim_hash_table_search(ZDimHashTable *self, guint num, gchar **keys); #endif zorp-3.9.5/lib/zorp/dispatch.h000066400000000000000000000053171172670260400162450ustar00rootroot00000000000000#ifndef ZORP_DISPATCH_H_INCLUDED #define ZORP_DISPATCH_H_INCLUDED #include #include #include #include typedef struct _ZDispatchEntry ZDispatchEntry; typedef struct _ZDispatchBind ZDispatchBind; /* dispatching priorities */ enum { ZD_PRI_LISTEN=100, /* used by listeners and receivers */ ZD_PRI_NORMAL=0, /* used by proxies supporting several subsessions for fastpath*/ ZD_PRI_RELATED=-100, /* used by proxies needing related connections, e.g. FTP data stream */ }; enum { ZD_BIND_NONE, ZD_BIND_SOCKADDR, ZD_BIND_IFACE, ZD_BIND_IFACE_GROUP, } ZDispatchBindType; typedef struct _ZDispatchCommonParams { gboolean threaded; gboolean mark_tproxy; gboolean transparent; } ZDispatchCommonParams; typedef struct _ZDispatchTCPParams { gboolean accept_one; /* prohibits other dispatch_registers */ gint backlog; /* listen backlog, the first dispatch registration counts */ } ZDispatchTCPParams; typedef struct _ZDispatchUDPParams { gint rcvbuf; } ZDispatchUDPParams; typedef struct _ZDispatchParams { ZDispatchCommonParams common; union { ZDispatchTCPParams tcp; ZDispatchUDPParams udp; }; } ZDispatchParams; typedef gboolean (*ZDispatchCallbackFunc)(ZConnection *conn, gpointer user_data); /* ZDispatchBind */ /* The dispatch_table hashtable contains ZDispatchEntry structures keyed * with instances of this type */ struct _ZDispatchBind { ZRefCount ref_cnt; gushort protocol; gushort type; union { struct { ZSockAddr *addr; } sa; struct { gchar iface[16]; gint family; struct in_addr ip4; gushort port; } iface; struct { guint32 group; gint family; gushort port; } iface_group; }; }; ZDispatchBind *z_dispatch_bind_new_sa(guint protocol, ZSockAddr *addr); ZDispatchBind *z_dispatch_bind_new_iface(guint protocol, const gchar *iface, gint family, const gchar *ip, guint port); ZDispatchBind *z_dispatch_bind_new_iface_group(guint protocol, guint32 group, gint family, guint port); gchar *z_dispatch_bind_format(ZDispatchBind *self, gchar *buf, gsize buflen); ZDispatchBind *z_dispatch_bind_ref(ZDispatchBind *self); void z_dispatch_bind_unref(ZDispatchBind *self); /* Dispatch main entry points */ ZDispatchEntry * z_dispatch_register(gchar *session_id, ZDispatchBind *key, ZSockAddr **bound_addr, gint prio, ZDispatchParams *params, ZDispatchCallbackFunc cb, gpointer user_data, GDestroyNotify data_destroy); void z_dispatch_unregister(ZDispatchEntry *de); void z_dispatch_init(void); void z_dispatch_destroy(void); #endif zorp-3.9.5/lib/zorp/idsconn.h000066400000000000000000000001021172670260400160660ustar00rootroot00000000000000#ifndef _LIBCONN_H_INCLUDED #define _LIBCONN_H_INCLUDED #endif zorp-3.9.5/lib/zorp/ifmonitor.h000066400000000000000000000046531172670260400164560ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_IFMONITOR_H_INCLUDED #define ZORP_IFMONITOR_H_INCLUDED #include typedef enum { Z_IFC_REMOVE, Z_IFC_ADD, } ZIfChangeType; typedef void (*ZIfmonWatchFunc)(const gchar *iface, ZIfChangeType change, gint family, void *addr, gpointer user_data); typedef struct _ZIfmonWatch ZIfmonWatch; gboolean z_ifmon_watch_iface_matches(ZIfmonWatch *w, const gchar *if_name); ZIfmonWatch *z_ifmon_register_watch(const gchar *iface, gint family, ZIfmonWatchFunc callback, gpointer user_data, GDestroyNotify user_data_destroy); void z_ifmon_unregister_watch(ZIfmonWatch *watch); typedef void (*ZIfmonGroupWatchFunc)(guint32 group, ZIfChangeType change, const gchar *if_name, gpointer user_data); typedef struct _ZIfmonGroupWatch ZIfmonGroupWatch; ZIfmonGroupWatch *z_ifmon_register_group_watch(guint32 group, ZIfmonGroupWatchFunc callback, gpointer user_data, GDestroyNotify user_data_destroy); void z_ifmon_unregister_group_watch(ZIfmonGroupWatch *watch); const void *z_ifmon_get_primary_address_by_name(const gchar *iface, gint family); const void *z_ifmon_get_primary_address(guint ifindex, gint family); gboolean z_ifmon_get_ifindex(const gchar *iface, guint *if_index); guint z_ifmon_get_iface_flags(guint ifindex); void z_ifmon_init(void); void z_ifmon_destroy(void); #endif zorp-3.9.5/lib/zorp/ipfiptproxy-kernel.h000066400000000000000000000000001172670260400203010ustar00rootroot00000000000000zorp-3.9.5/lib/zorp/kzorp-kernel.h000066400000000000000000000031061172670260400170630ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_KZORP_KERNEL_H_INCLUDED #define ZORP_KZORP_KERNEL_H_INCLUDED #define KZ_ATTR_NAME_MAX_LENGTH 1023 #define SO_KZORP_RESULT 1678333 struct z_kzorp_lookup_result { u_int64_t cookie; char czone_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; char szone_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; char dispatcher_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; char service_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; }; #endif zorp-3.9.5/lib/zorp/kzorp.h000066400000000000000000000025301172670260400156050ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_KZORP_H_INCLUDED #define ZORP_KZORP_H_INCLUDED #include gboolean z_kzorp_get_lookup_result(gint8 family, gint fd, struct z_kzorp_lookup_result *result); #endif zorp-3.9.5/lib/zorp/linebalance.h000066400000000000000000000045271172670260400167050ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Panther * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_LINEBALANCE_H_INCLUDED #define ZORP_LINEBALANCE_H_INCLUDED #include /* NOTE: For the meaning of this defines please see * http://specwiki.balabit/ZorpLineBalance. * And please keep it being syncronized. */ #define Z_LB_SHMEM_NAME "/zorpaddr" #define Z_LB_SHMEM_SIZE 9808 #define Z_LB_POLICY_NAME_MAX 256 #define Z_LB_POLICY_MAX 31 #define Z_LB_IFACE_MAX 7 typedef struct _ZorpBalanceIface { guint32 ip; guint32 pref; } ZorpBalanceIface; /* An inteface in a policy (group) */ typedef struct _ZorpBalancePolicyInterface { guint32 ipaddr; gint32 percent; } ZorpBalancePolicyInterface; typedef struct _ZorpBalancePolicy { gchar name[Z_LB_POLICY_NAME_MAX]; guint32 iface_num; ZorpBalancePolicyInterface ifaces[Z_LB_IFACE_MAX]; } ZorpBalancePolicy; typedef struct _ZorpBalanceStruct { time_t timestamp; guint32 policy_num; ZorpBalancePolicy policies[Z_LB_POLICY_MAX]; } ZorpBalanceStruct; /* This data type is mapped to the shared memory */ typedef struct _ZorpBalanceShmemData { guint32 size; ZorpBalanceStruct data; guint32 checksum; } ZorpBalanceShmemData; #endif zorp-3.9.5/lib/zorp/modules.h000066400000000000000000000023771172670260400161210ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_H_INCLUDED #define ZORP_MODULES_H_INCLUDED gint z_load_module(gchar *modname); #endif zorp-3.9.5/lib/zorp/nfdynexpect-kernel.h000066400000000000000000000016221172670260400202460ustar00rootroot00000000000000#ifndef _IP_CONNTRACK_DYNEXPECT_H #define _IP_CONNTRACK_DYNEXPECT_H #include #define SO_DYNEXPECT_MAP 11281 #define SO_DYNEXPECT_EXPECT 11282 #define SO_DYNEXPECT_DESTROY 11283 #define SO_DYNEXPECT_MARK 11284 struct ip_ct_dynexpect_map { u_int32_t mapping_id; u_int32_t orig_ip; u_int32_t new_ip; u_int16_t orig_port; u_int16_t n_ports; u_int16_t new_port; u_int8_t proto; u_int8_t _res1; u_int32_t n_active; } __attribute__((packed)); struct ip_ct_dynexpect_expect { u_int32_t mapping_id; u_int32_t peer_ip; u_int16_t peer_port; } __attribute__((packed)); struct ip_ct_dynexpect_destroy { u_int32_t mapping_id; } __attribute__((packed)); struct ip_ct_dynexpect_mark { u_int32_t mapping_id; u_int32_t mark; } __attribute__((packed)); #endif /* _IP_CONNTRACK_DYNEXPECT_H */ zorp-3.9.5/lib/zorp/nfiptproxy-kernel.h000066400000000000000000000017651172670260400201510ustar00rootroot00000000000000/* * Transparent proxy support for Linux/iptables * * Copyright (c) 2002 BalaBit IT Ltd. * Author: Balzs Scheidler * * This code is under GPLv2. */ #ifndef _IP_TPROXY_OLD_H #define _IP_TPROXY_OLD_H #ifdef __KERNEL__ #include #include #else #include #endif /* * used in setsockopt(SOL_IP, IP_TPROXY_*) should not collide * with values in */ #define IP_TPROXY_ASSIGN 20 #define IP_TPROXY_UNASSIGN 21 #define IP_TPROXY_QUERY 22 #define IP_TPROXY_FLAGS 23 #define IP_TPROXY_ALLOC 24 #define IP_TPROXY_CONNECT 25 /* bitfields in IP_TPROXY_FLAGS */ #define ITP_CONNECT 0x00000001 #define ITP_LISTEN 0x00000002 #define ITP_ESTABLISHED 0x00000004 #define ITP_ONCE 0x00010000 #define ITP_MARK 0x00020000 #define ITP_APPLIED 0x00040000 #define ITP_UNIDIR 0x00080000 /* structure passed to setsockopt(SOL_IP, IP_TPROXY) */ struct in_tproxy { struct in_addr itp_faddr; u_int16_t itp_fport; }; #endif zorp-3.9.5/lib/zorp/nfiptproxy-kernelv2.h000066400000000000000000000026301172670260400204110ustar00rootroot00000000000000/* * Transparent proxy support for Linux/iptables * * Copyright (c) 2002 BalaBit IT Ltd. * Author: Balzs Scheidler * * This code is under GPLv2. */ #ifndef _IP_TPROXY_H #define _IP_TPROXY_H #ifdef __KERNEL__ #include #include #else #include #ifndef IP_RECVORIGADDRS #define IP_RECVORIGADDRS 11273 #define IP_ORIGADDRS IP_RECVORIGADDRS struct in_origaddrs { struct in_addr ioa_srcaddr; struct in_addr ioa_dstaddr; unsigned short int ioa_srcport; unsigned short int ioa_dstport; }; #endif #endif /* * used in setsockopt(SOL_IP, IP_TPROXY) should not collide * with values in */ #define IP_TPROXY 11274 /* tproxy operations */ enum { TPROXY_VERSION = 0, TPROXY_ASSIGN, TPROXY_UNASSIGN, TPROXY_QUERY, TPROXY_FLAGS, TPROXY_ALLOC, TPROXY_CONNECT }; /* bitfields in IP_TPROXY_FLAGS */ #define ITP_CONNECT 0x00000001 #define ITP_LISTEN 0x00000002 #define ITP_ESTABLISHED 0x00000004 #define ITP_ONCE 0x00010000 #define ITP_MARK 0x00020000 #define ITP_APPLIED 0x00040000 #define ITP_UNIDIR 0x00080000 struct in_tproxy_addr { struct in_addr faddr; u_int16_t fport; }; struct in_tproxy { /* fixed part, should not change between versions */ u_int32_t op; /* extensible part */ union _in_args { u_int32_t version; struct in_tproxy_addr addr; u_int32_t flags; } v; }; #endif zorp-3.9.5/lib/zorp/notification.h000066400000000000000000000023441172670260400171310ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_NOTIFICATION_H_INCLUDED #define ZORP_NOTIFICATION_H_INCLUDED #endif zorp-3.9.5/lib/zorp/plugsession.h000066400000000000000000000047511172670260400170220ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PLUGSESSION_H_INCLUDED #define ZORP_PLUGSESSION_H_INCLUDED #include #include #include typedef struct _ZPlugSession ZPlugSession; typedef struct _ZPlugSessionData { gint timeout; gboolean copy_to_server, copy_to_client; gboolean shutdown_soft; guint buffer_size; guint packet_stats_interval_time, packet_stats_interval_packet; gboolean (*packet_stats)(ZPlugSession *self, guint64 client_bytes, guint64 client_pkts, guint64 server_bytes, guint64 server_pkts, gpointer user_data); void (*finish)(ZPlugSession *self, gpointer user_data); void (*timeout_cb) (ZPlugSession *self, gpointer user_data); } ZPlugSessionData; gboolean z_plug_session_start(ZPlugSession *self, ZPoll *poll); void z_plug_session_cancel(ZPlugSession *self); void z_plug_session_register_vars(ZPlugSession *self, ZPolicyDict *dict); ZPlugSession * z_plug_session_new(ZPlugSessionData *session_data, ZStream *client_stream, ZStream *server_stream, ZStackedProxy *stacked, gpointer user_data); void z_plug_session_destroy(ZPlugSession *self); ZPlugSession *z_plug_session_ref(ZPlugSession *self); void z_plug_session_unref(ZPlugSession *self); #endif zorp-3.9.5/lib/zorp/policy.h000066400000000000000000000165501172670260400157460ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_POLICY_H_INCLUDED #define ZORP_POLICY_H_INCLUDED #include #include /*+ ZPolicyObj is a reference counted data structure, capable of holding a value in the policy layer. Currently it's just a simple PyObject. +*/ typedef PyObject ZPolicyObj; typedef struct _ZPolicy ZPolicy; typedef struct _ZPolicyThread ZPolicyThread; struct _ZPolicy { gint ref_cnt; gchar *policy_filename; ZPolicyThread *main_thread; }; void z_policy_thread_ready(ZPolicyThread *self); void z_policy_thread_acquire(ZPolicyThread *self); void z_policy_thread_release(ZPolicyThread *self); ZPolicyThread *z_policy_thread_new(ZPolicy *policy); void z_policy_thread_destroy(ZPolicyThread *self); ZPolicyThread *z_policy_thread_self(void); ZPolicy *z_policy_thread_get_policy(ZPolicyThread *self); ZPolicy *z_policy_ref(ZPolicy *self); void z_policy_unref(ZPolicy *self); gboolean z_policy_boot(ZPolicy *self); gboolean z_policy_load(ZPolicy *self); gboolean z_policy_init(ZPolicy *self, gchar const **instance_name, gchar const *virtual_instance_name, gboolean is_master); ZPolicy *z_policy_new(const gchar *filename); void z_policy_acquire_main(ZPolicy *self); void z_policy_release_main(ZPolicy *self); gboolean z_policy_deinit(ZPolicy *self, gchar const **instance_name, gchar const *virtual_instance_name); gboolean z_policy_cleanup(ZPolicy *self, gchar const **instance_name, gchar const *virtual_instance_name, gboolean is_master); #define z_policy_exc_value_error PyExc_ValueError #define z_policy_exc_attribute_error PyExc_AttributeError #define z_policy_exc_runtime_error PyExc_RuntimeError void z_policy_raise_exception(gchar *exception_name, gchar *desc); void z_policy_raise_exception_obj(ZPolicyObj *exc, gchar *desc); #define z_policy_lock z_policy_thread_acquire #define z_policy_unlock z_policy_thread_release extern ZPolicy *current_policy; /* Zorp policy verdicts, determines what the proxy does with a specific event * Can be extended with proxy specific values above 100 */ typedef enum { ZV_UNSPEC = 0, /* policy doesn't specify it, do something sensible */ ZV_ACCEPT = 1, ZV_DENY = 2, ZV_REJECT = 3, /* continue and tell the client that we didn't do it */ ZV_ABORT = 4, /* abort the connection */ ZV_DROP = 5, /* continue and don't do it */ ZV_POLICY = 6, /* Policy level will decide what to do */ ZV_ERROR = 7, /* Error occured try to nice fail */ ZV_PROXY_SPECIFIC = 100, } ZVerdict; const gchar *z_verdict_str(ZVerdict verdict); #define z_policy_var_build(format, args...) Py_BuildValue(format, ##args) #define z_policy_var_str(v) PyObject_Str(v) #define z_policy_var_ref(v) do { Py_XINCREF(v); } while (0) #define z_policy_var_ref_nonnull(v) do { Py_INCREF(v); } while (0) #define z_policy_var_unref(v) do { Py_XDECREF(v); } while (0) #define z_policy_var_repr(v) PyObject_Repr(v) #define z_policy_none Py_None static inline PyObject * z_policy_none_ref(void) { z_policy_var_ref_nonnull(z_policy_none); return z_policy_none; } gboolean z_policy_var_parse_str(PyObject *val, gchar **result); gboolean z_policy_var_parse_boolean(PyObject *val, gboolean *result); gboolean z_policy_var_parse_int(PyObject *val, gint *result); gboolean z_policy_var_parse_uint(PyObject *val, guint *result); gboolean z_policy_var_parse_int64(PyObject *val, gint64 *result); gboolean z_policy_var_parse_uint64(PyObject *val, guint64 *result); gboolean z_policy_var_parse_size(PyObject *val, gsize *result); static inline gboolean z_policy_var_parse_ssize(PyObject *val, gssize *result) { return z_policy_var_parse_size(val, (gsize *) result); } #define z_policy_var_parse(v, format, args...) \ ({gboolean __res = PyArg_Parse(v, format, ##args); if (!__res) PyErr_Clear(); __res;}) #define z_policy_var_parse_tuple(v, format, args...) \ ({gboolean __res = PyArg_ParseTuple(v, format, ##args); if (!__res) PyErr_Clear(); __res;}) #define z_policy_tuple_new(s) PyTuple_New(s) #define z_policy_tuple_resize(s, n) PyTuple_Resize(s, n) #define z_policy_tuple_getitem(l, i) PyTuple_GetItem(l, i) #define z_policy_tuple_setitem(v, i, x) PyTuple_SetItem(v, i, x) #define z_policy_tuple_check(v) PyTuple_Check(v) #define z_policy_seq_check(v) PySequence_Check(v) #define z_policy_seq_getitem(v, i) PySequence_GetItem(v, i) #define z_policy_seq_length(v) PyObject_Length(v) #define z_policy_seq_get_slice(v, l, h) PySequence_GetSlice(v, l, h) #define z_policy_list_new(n) PyList_New(n) #define z_policy_list_getitem(l, i) PyList_GetItem(l, i) #define z_policy_list_setitem(l, i, x) PyList_SetItem(l, i, x) #define z_policy_list_append(l, x) PyList_Append(l, x) #define z_policy_list_size(l) PyList_Size(l) #define z_policy_list_set_slice(list, l, h, i) PyList_SetSlice(list, l, h, i) #define z_policy_pdict_check(v) PyDict_Check(v) #define z_policy_pdict_next(v, pos, k, vl) PyDict_Next(v, pos, k, vl) #define z_policy_pdict_new() PyDict_New() #define z_policy_pdict_getitem(v, i) PyDict_GetItem(v, i) #define z_policy_pdict_getitem_string(v, i) PyDict_GetItemString(v, i) #define z_policy_pdict_setitem(d, k, v) PyDict_SetItem(d, k, v) #define z_policy_pdict_setitem_string(d, k, v) PyDict_SetItemString(d, k, v) #define z_policy_int_new(n) PyInt_FromLong(n) #define z_policy_str_new(s) PyString_FromString(s) #define z_policy_str_check(v) PyString_Check(v) #define z_policy_str_as_string(v) PyString_AsString(v) #define z_policy_error_clear() PyErr_Clear() gboolean z_policy_tuple_get_verdict(ZPolicyObj *tuple, guint *verdict); ZPolicyObj *z_policy_convert_strv_to_list(gchar const **strv); ZPolicyObj *z_policy_call(ZPolicyObj *handler, char *name, ZPolicyObj *args, gboolean *called, gchar *session_id); int z_policy_event(ZPolicyObj *handler, char *name, ZPolicyObj *args, gchar *session_id); PyObject *z_policy_call_object(PyObject *func, PyObject *args, gchar *session_id); gint z_policy_setattr(ZPolicyObj *handler, char *name, ZPolicyObj *value); ZPolicyObj *z_policy_getattr(ZPolicyObj *handler, char *name); PyObject *z_session_getattr(PyObject *handler, char *name); PyObject *z_global_getattr(const gchar *name); #endif zorp-3.9.5/lib/zorp/proxy.h000066400000000000000000000347171172670260400156350ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PROXY_H_INCLUDED #define ZORP_PROXY_H_INCLUDED #include #include #include #include #include #include #include #include #include #include /* proxy states, the order of these enums is important */ enum { ZPS_INITIAL, ZPS_THREAD_STARTED, ZPS_CONFIG, ZPS_STARTING_UP, ZPS_WORKING, ZPS_SHUTTING_DOWN, ZPS_DESTROYING, }; /* proxy flags */ enum { ZPF_NONBLOCKING=0x0001, ZPF_STOP_REQUEST=0x0002, }; /* compatibility macros */ #define Z_VAR_GET 0x00000001 /* variable can be read */ #define Z_VAR_SET 0x00000003 /* variable can be read and written */ #define Z_VAR_GET_CONFIG 0x00000004 #define Z_VAR_SET_CONFIG 0x0000000C /* variable can be read and written */ #define Z_VAR_TYPE(t) ((t) & 0x0000ff00) #define Z_VAR_TYPE_INT 0x00000100 /* variable is an int */ #define Z_VAR_TYPE_STRING 0x00000200 /* variable is a string */ #define Z_VAR_TYPE_OBJECT 0x00000400 /* variable is a policy object */ #define Z_VAR_TYPE_HASH 0x00000500 /* variable is a hash */ #define Z_VAR_TYPE_METHOD 0x00000600 /* variable is a method */ #define Z_VAR_TYPE_CUSTOM 0x00000700 /* variable is something, requests are processed via a function call */ #define Z_VAR_TYPE_DIMHASH 0x00000800 /* variable is a multidimensional hash */ #define Z_VAR_TYPE_ALIAS 0x00000900 /* variable is an alias of another variable */ #define Z_VAR_TYPE_OBSOLETE 0x00000A00 /* variable is an obsolete alias of another variable */ #define Z_VAR_TYPE_INT64 0x00000B00 /* variable is an int64 */ /** The authentication type of z_proxy_user_authenticated() (#ZProxy.userAuthenticated()), indicating which type of authentication * occured before calling z_proxy_user_authenticated(). Its "auth_info" parameter is also changed */ typedef enum _ZProxyUserAuthType { Z_PROXY_USER_AUTHENTICATED_NONE, /**< The auth type is not explicitly specified, none of the others. * From Zorp, the auth_info parameter is "none", from Python this can be any other, too */ Z_PROXY_USER_AUTHENTICATED_INBAND, /**< The authentication is an inband authentication. Auth_info param is "inband". */ Z_PROXY_USER_AUTHENTICATED_GATEWAY, /**< The authentication is a gateway authentication. Auth_info param is "gw-auth". */ Z_PROXY_USER_AUTHENTICATED_SERVER, /**< The authentication is a server-side authentication. Auth_info param is "server". */ } ZProxyUserAuthType; typedef struct _ZProxyParams ZProxyParams; typedef struct _ZProxyIface ZProxyIface; typedef struct _ZProxyFuncs ZProxyFuncs; typedef struct _ZProxyGroup ZProxyGroup; typedef struct _ZChannelProps ZChannelProps; struct _ZProxyParams { const gchar *session_id; ZPolicyObj *pyclient; ZStream *client; ZPolicyObj *handler; ZProxy *parent; }; struct _ZProxyFuncs { ZObjectFuncs super; gboolean (*config)(ZProxy *self); gboolean (*startup)(ZProxy *self); void (*main)(ZProxy *self); void (*shutdown)(ZProxy *self); void (*destroy)(ZProxy *self); gboolean (*nonblocking_init)(ZProxy *self, ZPoll *poll); void (*nonblocking_deinit)(ZProxy *self); void (*wakeup)(ZProxy *self); }; struct _ZChannelProps { guint8 tos[EP_DIR_MAX]; }; struct _ZProxy { ZObject super; gchar session_id[MAX_SESSION_ID]; ZThread *proxy_thread; GThreadPriority proxy_pri; guint16 status; guint16 flags; ZPolicyThread *thread; ZPolicyObj *handler; ZPolicyDict *dict; ZProxyGroup *group; ZStream *endpoints[EP_MAX]; ZPolicyObj *py_endpoints[EP_MAX]; GString *language; /* a pointer to the parent proxy */ ZProxy *parent_proxy; /* the linked list of child proxies */ GList *child_proxies; GStaticMutex interfaces_lock; GList *interfaces; ZProxySsl ssl_opts; gboolean channel_props_set[EP_MAX]; ZChannelProps channel_props[EP_MAX]; }; extern ZClass ZProxy__class; /* function prototypes registered in the registry */ typedef ZProxy *(*ZProxyCreateFunc)(ZProxyParams *params); /* log functions */ #define z_proxy_log(self, class, level, format, args...) \ do { \ z_object_check_compatible((ZObject *) self, Z_CLASS(ZProxy)); \ /*NOLOG*/ \ z_log(((ZProxy *) self)->session_id, class, level, format, ##args); \ } while (0) #define z_proxy_log_data_dump(self, class, level, buf, len) \ do { \ z_object_check_compatible((ZObject *) self, Z_CLASS(ZProxy)); \ /*NOLOG*/ \ z_log_data_dump(((ZProxy *)self)->session_id, class, level, buf, len); \ } while (0) #define z_proxy_pktbuf_data_dump(self, class, level, pktbuf) \ do { \ z_object_check_compatible((ZObject *) self, Z_CLASS(ZProxy)); \ /*NOLOG*/ \ z_pktbuf_data_dump(((ZProxy *)self)->session_id, class, level, pktbuf); \ } while (0) #define z_proxy_log_text_dump(self, class, level, buf, len) \ do { \ z_object_check_compatible((ZObject *) self, Z_CLASS(ZProxy)); \ /*NOLOG*/ \ z_log_text_dump(((ZProxy *)self)->session_id, class, level, buf, len); \ } while (0) #if ENABLE_TRACE #define z_proxy_trace(self, args...) z_proxy_log(self , CORE_TRACE, 7, ##args) #define z_proxy_enter(self) z_session_enter(((ZProxy *)self)->session_id) #define z_proxy_leave(self) z_session_leave(((ZProxy *)self)->session_id) #define z_proxy_cp(self) z_session_cp(((ZProxy *)self)->session_id) #else #define z_proxy_trace(self, args...) ({void *__p G_GNUC_UNUSED = self;}) #define z_proxy_enter(self) ({void *__p G_GNUC_UNUSED = self;}) #define z_proxy_leave(self) #define z_proxy_cp(self) ({void *__p G_GNUC_UNUSED = self;}) #endif #define z_proxy_return(self, ...) do { z_proxy_leave(self); return __VA_ARGS__; } while (0) /* interface support */ void z_proxy_add_iface(ZProxy *self, ZProxyIface *iface); void z_proxy_del_iface(ZProxy *self, ZProxyIface *iface); ZProxyIface *z_proxy_find_iface(ZProxy *self, ZClass *compat); /* helper functions for communicating with the policy layer */ gboolean z_proxy_policy_config(ZProxy *); gboolean z_proxy_policy_startup(ZProxy *); void z_proxy_policy_shutdown(ZProxy *); void z_proxy_policy_destroy(ZProxy *self); /* compatibility functions for ZPolicyDict based attribute handling */ void z_proxy_var_new(ZProxy *self, const gchar *name, guint flags, ...); gboolean z_proxy_add_child(ZProxy *self, ZProxy *child_proxy); gboolean z_proxy_del_child(ZProxy *self, ZProxy *child_proxy); ZProxyGroup *z_proxy_get_group(ZProxy *self); void z_proxy_set_group(ZProxy *self, ZProxyGroup *proxy_group); /* misc helper functions */ gboolean z_proxy_set_server_address(ZProxy *self, const gchar *host, gint port); gint z_proxy_connect_server(ZProxy *self, const gchar *host, gint port); gint z_proxy_user_authenticated(ZProxy *self, const gchar *entity, gchar const **groups, ZProxyUserAuthType type); /** Wrapper for z_proxy_user_authenticated() with the default #Z_PROXY_USER_AUTHENTICATED_INBAND parameter. * @see z_proxy_user_authenticated() */ static inline gint z_proxy_user_authenticated_default(ZProxy *self, const gchar *entity, gchar const **groups) { return z_proxy_user_authenticated(self, entity, groups, Z_PROXY_USER_AUTHENTICATED_INBAND); } gboolean z_proxy_get_addresses(ZProxy *self, guint *protocol, ZSockAddr **client_address, ZSockAddr **client_local, ZSockAddr **server_address, ZSockAddr **server_local, ZDispatchBind **client_listen); gboolean z_proxy_get_addresses_locked(ZProxy *self, guint *protocol, ZSockAddr **client_address, ZSockAddr **client_local, ZSockAddr **server_address, ZSockAddr **server_local, ZDispatchBind **client_listen); gboolean z_proxy_threaded_start(ZProxy *self, ZProxyGroup *group); gboolean z_proxy_nonblocking_start(ZProxy *self, ZProxyGroup *group); void z_proxy_nonblocking_stop(ZProxy *self); gboolean z_proxy_check_license(ZProxy *self); static inline gboolean z_proxy_stop_requested(ZProxy *self) { return self->flags & ZPF_STOP_REQUEST; } gboolean z_proxy_loop_iteration(ZProxy *self); /* constructor for ZProxy */ ZProxy * z_proxy_new(ZClass *class, ZProxyParams *params); /* free method for ZProxy */ void z_proxy_free_method(ZObject *s); static inline gboolean z_proxy_config(ZProxy *self) { return Z_FUNCS(self, ZProxy)->config(self); } static inline gboolean z_proxy_startup(ZProxy *self) { return Z_FUNCS(self, ZProxy)->startup(self); } static inline void z_proxy_main(ZProxy *self) { Z_FUNCS(self, ZProxy)->main(self); } static inline void z_proxy_shutdown(ZProxy *self) { Z_FUNCS(self, ZProxy)->shutdown(self); } static inline void z_proxy_destroy(ZProxy *self) { Z_FUNCS(self, ZProxy)->destroy(self); } static inline gboolean z_proxy_nonblocking_init(ZProxy *self, ZPoll *poll) { return Z_FUNCS(self, ZProxy)->nonblocking_init(self, poll); } static inline void z_proxy_nonblocking_deinit(ZProxy *self) { Z_FUNCS(self, ZProxy)->nonblocking_deinit(self); } static inline void z_proxy_wakeup(ZProxy *self) { Z_FUNCS(self, ZProxy)->wakeup(self); } static inline ZProxy * z_proxy_ref(ZProxy *self) { return (ZProxy *) z_object_ref(&self->super); } static inline void z_proxy_unref(ZProxy *self) { z_object_unref(&self->super); } static inline void z_proxy_set_state(ZProxy *self, guint8 new_state) { self->status = (self->status & ~0xFF) | new_state; } static inline guint8 z_proxy_get_state(ZProxy *self) { return self->status & 0xFF; } /* Root class for proxy specific interfaces */ struct _ZProxyIface { ZObject super; ZProxy *owner; }; typedef ZObjectFuncs ZProxyIfaceFuncs; extern ZClass ZProxyIface__class; ZProxyIface *z_proxy_iface_new(ZClass *class, ZProxy *proxy); void z_proxy_iface_free_method(ZObject *s); /* ZProxyBasicIface */ /* NOTE: this interface is implemented by all proxies. dynamic references to * self->parent should only be used through this interface as in this case it * is ensured that the parent proxy exists by the time it is referenced. */ typedef ZProxyIface ZProxyBasicIface; typedef struct _ZProxyBasicIfaceFuncs { ZObjectFuncs super; gboolean (*get_var)(ZProxyBasicIface *self, const gchar *var_name, gchar **value); gboolean (*set_var)(ZProxyBasicIface *self, const gchar *var_name, gchar *value); } ZProxyBasicIfaceFuncs; extern ZClass ZProxyBasicIface__class; static inline gboolean z_proxy_basic_iface_get_var(ZProxyBasicIface *self, const gchar *var_name, gchar **value) { return Z_FUNCS(self, ZProxyBasicIface)->get_var(self, var_name, value); } static inline gboolean z_proxy_basic_iface_set_var(ZProxyBasicIface *self, const gchar *var_name, gchar *value) { return Z_FUNCS(self, ZProxyBasicIface)->set_var(self, var_name, value); } ZProxyBasicIface *z_proxy_basic_iface_new(ZClass *class, ZProxy *proxy); #define z_proxy_basic_iface_free_method z_proxy_iface_free_method /* ZProxyStackIface */ typedef ZProxyIface ZProxyStackIface; typedef struct _ZProxyStackIfaceFuncs { ZObjectFuncs super; void (*set_verdict)(ZProxyStackIface *self, ZVerdict verdict, const gchar *description); gboolean (*get_content_hint)(ZProxyStackIface *self, gint64 *content_length, const gchar **content_format); void (*set_content_hint)(ZProxyStackIface *self, gint64 content_length); } ZProxyStackIfaceFuncs; extern ZClass ZProxyStackIface__class; static inline void z_proxy_stack_iface_set_verdict(ZProxyStackIface *self, ZVerdict verdict, const gchar *description) { z_proxy_log(self->owner, CORE_INFO, 4, "Received verdict from stacked proxy; verdict='%s', description='%s'", z_verdict_str(verdict), description); Z_FUNCS(self, ZProxyStackIface)->set_verdict(self, verdict, description); } static inline void z_proxy_stack_iface_set_content_hint(ZProxyStackIface *self, gint64 content_length) { if (Z_FUNCS(self, ZProxyStackIface)->set_content_hint) Z_FUNCS(self, ZProxyStackIface)->set_content_hint(self, content_length); } static inline gboolean z_proxy_stack_iface_get_content_hint(ZProxyStackIface *self, gint64 *content_length, const gchar **content_format) { if (Z_FUNCS(self, ZProxyStackIface)->get_content_hint) return Z_FUNCS(self, ZProxyStackIface)->get_content_hint(self, content_length, content_format); *content_length = -1; *content_format = "file"; return TRUE; } #define z_proxy_stack_iface_new z_proxy_iface_new #define z_proxy_stack_iface_free_method z_proxy_iface_free_method typedef ZProxyIface ZProxyHostIface; typedef struct _ZProxyHostIfaceFuncs { ZObjectFuncs super; gboolean (*check_name)(ZProxyHostIface *s, const gchar *host_name, gchar *reason_buf, gsize reason_len); } ZProxyHostIfaceFuncs; extern ZClass ZProxyHostIface__class; static inline gboolean z_proxy_host_iface_check_name(ZProxyHostIface *s, const gchar *host_name, gchar *reason_buf, gsize reason_len) { return Z_FUNCS(s, ZProxyHostIface)->check_name(s, host_name, reason_buf, reason_len); } gboolean z_proxy_stop_request(const gchar *session_id); void z_proxy_hash_init(void); void z_proxy_hash_destroy(void); #endif zorp-3.9.5/lib/zorp/proxycommon.h000066400000000000000000000033341172670260400170350ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Laszlo Attila Toth * ***************************************************************************/ #ifndef ZORP_PROXY_COMMON_H_INCLUDED #define ZORP_PROXY_COMMON_H_INCLUDED /* a two-way connection between streams */ /* endpoint indexes */ typedef enum _ZEndpoint { EP_CLIENT, EP_SERVER, EP_MAX } ZEndpoint; #define EP_OTHER(ep) (1-(ep)) #define EP_STR(ep) ((ep) == EP_CLIENT ? "client" : "server") typedef enum _ZDirection { EP_DIR_IN, EP_DIR_OUT, EP_DIR_MAX } ZDirection; #define EP_DIR_OTHER(ep) (1-(ep)) #define EP_DIR_STR(ep) ((ep) == EP_DIR_IN ? "input" : "output") typedef struct _ZProxy ZProxy; #endif zorp-3.9.5/lib/zorp/proxygroup.h000066400000000000000000000034661172670260400167070ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PROXYGROUP_H_INCLUDED #define ZORP_PROXYGROUP_H_INCLUDED #include #include #include GMainContext *z_proxy_group_get_context(ZProxyGroup *self); ZPoll *z_proxy_group_get_poll(ZProxyGroup *self); gboolean z_proxy_group_start_session(ZProxyGroup *self, ZProxy *proxy); void z_proxy_group_stop_session(ZProxyGroup *self, ZProxy *proxy); gboolean z_proxy_group_iteration(ZProxyGroup *self); void z_proxy_group_wakeup(ZProxyGroup *self); ZProxyGroup *z_proxy_group_new(); void z_proxy_group_orphan(ZProxyGroup *self); ZProxyGroup *z_proxy_group_ref(ZProxyGroup *self); void z_proxy_group_unref(ZProxyGroup *self); #endif zorp-3.9.5/lib/zorp/proxyssl.h000066400000000000000000000100461172670260400163440ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2009, 2010 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * * Author: Laszlo Attila Toth * ***************************************************************************/ #ifndef ZORP_PROXY_SSL_H_INCLUDED #define ZORP_PROXY_SSL_H_INCLUDED #include #include typedef enum { PROXY_SSL_VERIFY_NONE = 0, PROXY_SSL_VERIFY_OPTIONAL_UNTRUSTED = 1, #define PROXY_SSL_VERIFY_OPTIONAL PROXY_SSL_VERIFY_OPTIONAL_UNTRUSTED PROXY_SSL_VERIFY_OPTIONAL_TRUSTED = 2, PROXY_SSL_VERIFY_REQUIRED_UNTRUSTED = 3, PROXY_SSL_VERIFY_REQUIRED_TRUSTED = 4, } proxy_ssl_verify_type; #define PROXY_SSL_HS_CLIENT_SERVER 0 #define PROXY_SSL_HS_SERVER_CLIENT 1 #define PROXY_SSL_HS_POLICY ZV_POLICY #define PROXY_SSL_HS_ACCEPT ZV_ACCEPT #define PROXY_SSL_HS_VERIFIED 10 typedef enum { PROXY_SSL_SEC_NONE = 0, PROXY_SSL_SEC_FORCE_SSL = 1, PROXY_SSL_SEC_ACCEPT_STARTTLS = 2, PROXY_SSL_SEC_FORWARD_STARTTLS = 3, } proxy_ssl_security_type; typedef struct _ZProxySsl { ZPolicyDict *dict; ZPolicyObj *ssl_struct; proxy_ssl_security_type security[EP_MAX]; GString *ssl_method[EP_MAX]; GString *ssl_cipher[EP_MAX]; ZSSLSession *ssl_sessions[EP_MAX]; ZPolicyObj *server_setup_key_cb, *server_setup_ca_list_cb, *server_setup_crl_list_cb, *server_verify_cert_cb; ZPolicyObj *client_setup_key_cb, *client_setup_ca_list_cb, *client_setup_crl_list_cb, *client_verify_cert_cb; EVP_PKEY *local_privkey[EP_MAX]; X509 *peer_cert[EP_MAX]; X509 *local_cert[EP_MAX]; STACK_OF(X509) *local_ca_list[EP_MAX]; STACK_OF(X509_NAME) *server_peer_ca_list; STACK_OF(X509_CRL) *local_crl_list[EP_MAX]; GString *verify_ca_directory[EP_MAX]; GString *verify_crl_directory[EP_MAX]; gboolean force_connect_at_handshake; gint handshake_timeout; gint handshake_seq; gboolean handshake_pending[EP_MAX]; GHashTable *handshake_hash[EP_MAX]; proxy_ssl_verify_type verify_type[EP_MAX]; int verify_depth[EP_MAX]; gboolean disable_proto_sslv2[EP_MAX]; gboolean disable_proto_sslv3[EP_MAX]; gboolean disable_proto_tlsv1[EP_MAX]; gboolean permit_invalid_certificates; gboolean permit_missing_crl; gboolean server_check_subject; GString *local_privkey_passphrase[EP_MAX]; /* List of handshake objects. Unfortunately OpenSSL callbacks cannot be * handed a destroy_notify callback so we generally cannot use * refcounting to manage the lifetime of handshake objects. * * Instead, we do store all handshake objects in this linked list in the * associated proxy and make sure we delete these when we can guarantee that * the handshake is no longer needed (referenced). * * Right now this means we delete handshake objects only from the proxy * destroy method. */ GList *handshakes; } ZProxySsl; struct _ZProxySSLHandshake; typedef void (*ZProxySSLCallbackFunc)(struct _ZProxySSLHandshake *hs, gpointer user_data); typedef struct _ZProxySSLHandshake { ZSSLSession *session; ZStream *stream; ZProxy *proxy; gint side; /* result */ gint ssl_err; gchar ssl_err_str[512]; /* internals */ GSource *timeout; ZStreamContext stream_context; ZProxySSLCallbackFunc completion_cb; gpointer completion_user_data; GDestroyNotify completion_user_data_notify; SSL_CTX *ssl_context; } ZProxySSLHandshake; ZProxySSLHandshake *z_proxy_ssl_handshake_new(ZProxy *proxy, ZStream *stream, gint side); void z_proxy_ssl_config_defaults(ZProxy *self); void z_proxy_ssl_register_vars(ZProxy *self); void z_proxy_ssl_free_vars(ZProxy *self); gboolean z_proxy_ssl_perform_handshake(ZProxySSLHandshake *handshake); gboolean z_proxy_ssl_init_stream(ZProxy *self, gint side); gboolean z_proxy_ssl_init_stream_nonblocking(ZProxy *self, gint side); gboolean z_proxy_ssl_request_handshake(ZProxy *self, gint side, gboolean forced); void z_proxy_ssl_clear_session(ZProxy *self, gint side); void z_proxy_ssl_set_force_connect_at_handshake(ZProxy *self, gboolean val); #endif zorp-3.9.5/lib/zorp/proxysslhostiface.h000066400000000000000000000025211172670260400202310ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PROXYSSLHOSTIFACE_H_INCLUDED #define ZORP_PROXYSSLHOSTIFACE_H_INCLUDED extern ZClass ZProxySslHostIface__class; ZProxyIface *z_proxy_ssl_host_iface_new(ZProxy *owner); #endif zorp-3.9.5/lib/zorp/proxystack.h000066400000000000000000000043601172670260400166520ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PROXYSTACK_H_INCLUDED #define ZORP_PROXYSTACK_H_INCLUDED #include #include typedef struct _ZStackedProxy ZStackedProxy; enum { Z_SPF_HALF_DUPLEX=0x0001 }; /* structure describing a stacked proxy instance */ struct _ZStackedProxy { ZRefCount ref_cnt; GStaticMutex destroy_lock; gboolean destroyed; guint32 flags; ZStream *downstreams[EP_MAX]; ZStream *control_stream; ZCPContext *control_proto; ZProxy *proxy; ZProxy *child_proxy; }; enum { Z_STACK_PROXY = 1, Z_STACK_PROGRAM = 2, Z_STACK_REMOTE = 3, Z_STACK_PROVIDER = 4, Z_STACK_CUSTOM = 5, }; gboolean z_proxy_stack_remote_handshake(ZSockAddr *sa, const gchar *stack_info, ZStream **client, ZStream **server, ZStream **control, guint32 *stack_flags); gboolean z_proxy_stack_object(ZProxy *self, ZPolicyObj *stack_obj, ZStackedProxy **stacked, ZPolicyDict *stack_info); ZStackedProxy *z_stacked_proxy_new(ZStream *client_stream, ZStream *server_stream, ZStream *control_stream, ZProxy *proxy, ZProxy *child_proxy, guint32 flags); void z_stacked_proxy_destroy(ZStackedProxy *self); #endif zorp-3.9.5/lib/zorp/pyattach.h000066400000000000000000000024411172670260400162560ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYATTACH_H_INCLUDED #define ZORP_PYATTACH_H_INCLUDED #include void z_policy_attach_module_init(void); #endif zorp-3.9.5/lib/zorp/pyaudit.h000066400000000000000000000024431172670260400161220ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Panther * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_PYAUDIT_H_INCLUDED #define ZORP_PYAUDIT_H_INCLUDED #endif zorp-3.9.5/lib/zorp/pybalance.h000066400000000000000000000024361172670260400164030ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYBALANCE_H_INCLUDED #define ZORP_PYBALANCE_H_INCLUDED #include void z_py_zorp_balance_init(void); #endif zorp-3.9.5/lib/zorp/pyblacklist.h000066400000000000000000000000001172670260400167470ustar00rootroot00000000000000zorp-3.9.5/lib/zorp/pycore.h000066400000000000000000000024241172670260400157430ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYCORE_H_INCLUDED #define ZORP_PYCORE_H_INCLUDED #include void z_py_zorp_core_init(void); #endif zorp-3.9.5/lib/zorp/pydict.h000066400000000000000000000103171172670260400157360ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_PYDICT_H_INCLUDED #define ZORP_PYDICT_H_INCLUDED #include #include typedef struct _ZPolicyDict ZPolicyDict; typedef enum { Z_VT_NONE = 0, /* end of argument list */ Z_VT_INT, /* variable is an int */ Z_VT_INT8, /* variable is an int8 */ Z_VT_INT16, /* variable is an int16 */ Z_VT_INT32, /* variable is an int32 */ Z_VT_INT64, /* variable is an int64 */ Z_VT_STRING, /* variable is a string, represented as a GString */ Z_VT_CSTRING, /* variable is a string, represented as a C character buffer and size */ Z_VT_IP, /* variable is an ip address, represented as struct in_addr */ Z_VT_IP6, /* variable is an ipv6 address, represented as struct in6_addr */ Z_VT_OBJECT, /* variable is a policy object */ Z_VT_HASH, /* variable is a hash */ Z_VT_METHOD, /* variable is a method */ Z_VT_CUSTOM, /* variable is something, requests are processed via a function call */ Z_VT_DIMHASH, /* variable is a multidimensional hash */ Z_VT_ALIAS, /* variable is an alias of another variable */ Z_VT_PTR, /* variable is a generic pointer */ } ZVarType; enum { /* access modes */ Z_VF_READ = 0x0001, Z_VF_WRITE = 0x0002, Z_VF_RW = 0x0003, Z_VF_CFG_READ = 0x0004, Z_VF_CFG_WRITE = 0x0008, Z_VF_CFG_RW = 0x000C, /* other flags */ Z_VF_OBSOLETE = 0x0010, /* */ Z_VF_DUP = 0x0020, /* dup value to an internal storage, requires Z_VF_CONST */ Z_VF_LITERAL = 0x0040, /* value is specified as a value instead of a pointer pointing somewhere else */ Z_VF_CONSUME = 0x0080, /* value should be freed by ZPolicyDict */ /* type specific flags */ Z_VF_IP_STR = 0x0100, /* represent an IP address as string */ Z_VF_INT_NET = 0x0200 /* represent integer in network byte order */ }; typedef ZPolicyObj *(*ZPolicyDictMethodFunc)(gpointer user_data, ZPolicyObj *args, ZPolicyObj *kw); typedef void (*ZPolicyDictIterFunc)(ZPolicyDict *self, const gchar *name, gpointer user_data); void z_policy_dict_wrap(ZPolicyDict *self, ZPolicyObj *wrapper); void z_policy_dict_unwrap(ZPolicyDict *self, ZPolicyObj *wrapper); ZPolicyObj *z_policy_dict_get_value(ZPolicyDict *self, gboolean is_config, const gchar *name); gint z_policy_dict_set_value(ZPolicyDict *self, gboolean is_config, const gchar *name, ZPolicyObj *new_value); ZPolicyObj *z_policy_dict_get_dict(ZPolicyDict *self); void z_policy_dict_register(ZPolicyDict *self, ZVarType first_var, ...); void z_policy_dict_set_app_data(ZPolicyDict *self, gpointer data, GDestroyNotify data_free); gpointer z_policy_dict_get_app_data(ZPolicyDict *self); void z_policy_dict_iterate(ZPolicyDict *self, ZPolicyDictIterFunc iter, gpointer user_data); ZPolicyDict *z_policy_dict_new(void); ZPolicyDict *z_policy_dict_ref(ZPolicyDict *self); void z_policy_dict_unref(ZPolicyDict *self); void z_policy_dict_destroy(ZPolicyDict *self); #endif zorp-3.9.5/lib/zorp/pydispatch.h000066400000000000000000000033011172670260400166050ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYDISPATCH_H_INCLUDED #define ZORP_PYDISPATCH_H_INCLUDED #include #include #include typedef struct _ZPolicyDispatchBind ZPolicyDispatchBind; ZDispatchBind * z_policy_dispatch_bind_get_db(ZPolicyObj *self); static inline gboolean z_policy_dispatch_bind_check(ZPolicyObj *self) { return z_policy_struct_check(self, Z_PST_DB_SOCKADDR) || z_policy_struct_check(self, Z_PST_DB_IFACE) || z_policy_struct_check(self, Z_PST_DB_IFACE_GROUP); } void z_policy_dispatch_module_init(void); #endif zorp-3.9.5/lib/zorp/pyproxy.h000066400000000000000000000032131172670260400161710ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYPROXY_H_INCLUDED #define ZORP_PYPROXY_H_INCLUDED #include #include #include typedef struct _ZPolicyProxy ZPolicyProxy; extern PyTypeObject z_policy_proxy_type; gboolean z_policy_proxy_bind_implementation(PyObject *self); ZProxy *z_policy_proxy_get_proxy(PyObject *obj); void z_policy_proxy_module_init(void); static inline gboolean z_policy_proxy_check(ZPolicyObj *s) { return PyObject_TypeCheck(s, &z_policy_proxy_type); } #endif zorp-3.9.5/lib/zorp/pyproxygroup.h000066400000000000000000000031161172670260400172500ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYPROXYGROUP_H_INCLUDED #define ZORP_PYPROXYGROUP_H_INCLUDED #include #include ZPolicyObj *z_policy_proxy_group_new(gint max_secondary_sessions); ZProxyGroup *z_policy_proxy_group_get_proxy_group(ZPolicyObj *s); static inline gboolean z_policy_proxy_group_check(ZPolicyObj *s) { return z_policy_struct_check(s, Z_PST_PROXY_GROUP); } void z_policy_proxy_group_module_init(void); #endif zorp-3.9.5/lib/zorp/pysatyr.h000066400000000000000000000022221172670260400161510ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ zorp-3.9.5/lib/zorp/pysockaddr.h000066400000000000000000000032101172670260400165770ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYSOCKADDR_H_INCLUDED #define ZORP_PYSOCKADDR_H_INCLUDED #include #include ZPolicyObj *z_policy_sockaddr_new(ZSockAddr *sa); ZSockAddr *z_policy_sockaddr_get_sa(ZPolicyObj *s); static inline gboolean z_policy_sockaddr_check(ZPolicyObj *s) { return z_policy_struct_check(s, Z_PST_SOCKADDR_INET) || z_policy_struct_check(s, Z_PST_SOCKADDR_UNIX) || z_policy_struct_check(s, Z_PST_SOCKADDR_INET6); } void z_policy_sockaddr_module_init(void); #endif zorp-3.9.5/lib/zorp/pystream.h000066400000000000000000000034671172670260400163160ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_PYSTREAM_H_INCLUDED #define ZORP_PYSTREAM_H_INCLUDED #include #include /*+ ZPolicyStream is the Python interface to ZStream. +*/ typedef struct _ZPolicyStream { PyObject_HEAD ZStream *stream; } ZPolicyStream; extern PyTypeObject z_policy_stream_type; #define z_policy_stream_check(ob) ((ob)->ob_type == &z_policy_stream_type) void z_policy_stream_module_init(void); PyObject *z_policy_stream_new(ZStream *Stream); static inline ZStream * z_policy_stream_get_stream(PyObject *s) { ZPolicyStream *self = (ZPolicyStream *) s; g_assert(z_policy_stream_check(s)); return z_stream_ref(self->stream); } #endif zorp-3.9.5/lib/zorp/pystruct.h000066400000000000000000000044131172670260400163370ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_PYSTRUCT_H_INCLUDED #define ZORP_PYSTRUCT_H_INCLUDED #include #include #include typedef struct _ZPolicyStruct ZPolicyStruct; enum { Z_PST_NONE = 0, /* shared type for multiple PyStructs, cannot be distinguished from Python */ Z_PST_SHARED, Z_PST_SOCKADDR, Z_PST_SOCKADDR_INET, Z_PST_SOCKADDR_INET6, Z_PST_SOCKADDR_UNIX, Z_PST_DISPATCH_BIND, Z_PST_DB_SOCKADDR, Z_PST_DB_IFACE, Z_PST_DB_IFACE_GROUP, Z_PST_PROXY_GROUP, Z_PST_MAX, }; typedef ZPolicyObj *(*ZPolicyStructFormatFunc)(ZPolicyObj *s); void z_policy_struct_set_format(ZPolicyObj *s, ZPolicyStructFormatFunc format); gboolean z_policy_struct_check(ZPolicyObj *s, gint type); void z_policy_struct_set_is_config(ZPolicyObj *s, gboolean is_config); ZPolicyDict *z_policy_struct_get_dict(ZPolicyObj *s); ZPolicyDict *z_policy_struct_release_dict(ZPolicyObj *s); ZPolicyObj *z_policy_struct_new(ZPolicyDict *dict, gint type); ZPolicyObj *z_policy_struct_get_type_object(gint type); void z_policy_struct_module_init(void); #endif zorp-3.9.5/lib/zorp/pyx509.h000066400000000000000000000041151172670260400155170ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Panther * ***************************************************************************/ #ifndef ZORP_PYX509_H_INCLUDED #define ZORP_PYX509_H_INCLUDED #include #include ZPolicyObj *z_py_ssl_privkey_get(ZProxy *self, gchar *name, gpointer value); int z_py_ssl_privkey_set(ZProxy *self, gchar *name, gpointer value, ZPolicyObj *new); void z_py_ssl_privkey_free(gpointer value); ZPolicyObj *z_py_ssl_certificate_get(ZProxy *self, gchar *name, gpointer value); int z_py_ssl_certificate_set(ZProxy *self, gchar *name, gpointer value, ZPolicyObj *new); void z_py_ssl_certificate_free(gpointer value); ZPolicyObj *z_py_ssl_cert_list_get(ZProxy *self, gchar *name, gpointer value); void z_py_ssl_cert_list_free(gpointer value); ZPolicyObj *z_py_ssl_cert_name_list_get(ZProxy *self, gchar *name, gpointer value); void z_py_ssl_cert_name_list_free(gpointer value); ZPolicyObj *z_py_ssl_crl_list_get(ZProxy *self, gchar *name, gpointer value); void z_py_ssl_crl_list_free(gpointer value); #endif zorp-3.9.5/lib/zorp/pyzasauth.h000066400000000000000000000022221172670260400164660ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ zorp-3.9.5/lib/zorp/satyr.h000066400000000000000000000022221172670260400156000ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ zorp-3.9.5/lib/zorp/sysdep.h000066400000000000000000000012051172670260400157450ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * ***************************************************************************/ #ifndef ZORP_SYSDEP_H_INCLUDED #define ZORP_SYSDEP_H_INCLUDED #include #define Z_SD_TPROXY_LINUX22 1 #define Z_SD_TPROXY_NETFILTER_V12 2 #define Z_SD_TPROXY_IPF 3 #define Z_SD_TPROXY_NETFILTER_V20 4 #define Z_SD_TPROXY_NETFILTER_V30 5 #define Z_SD_TPROXY_NETFILTER_V40 6 gboolean z_sysdep_init(const gchar *sysdep_tproxy_arg); void z_sysdep_destroy(void); #endif zorp-3.9.5/lib/zorp/szig.h000066400000000000000000000074141172670260400154220ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_SZIG_H_INCLUDED #define ZORP_SZIG_H_INCLUDED #include /* these values are copied to Python, change carefully */ enum { Z_SZIG_THREAD_START = 0, Z_SZIG_THREAD_STOP, Z_SZIG_TICK, Z_SZIG_COUNTED_IP, Z_SZIG_CONNECTION_PROPS, Z_SZIG_CONNECTION_STOP, Z_SZIG_AUDIT_START, Z_SZIG_AUDIT_STOP, Z_SZIG_RELOAD, Z_SZIG_AUTH_PENDING_BEGIN, Z_SZIG_AUTH_PENDING_FINISH, Z_SZIG_SERVICE_COUNT, Z_SZIG_CONNECTION_START, Z_SZIG_MAX }; /* these values are copied to Python, change carefully */ enum { Z_SZIG_TYPE_NOTINIT = 0, Z_SZIG_TYPE_LONG, Z_SZIG_TYPE_TIME, Z_SZIG_TYPE_STRING, Z_SZIG_TYPE_PROPS, Z_SZIG_TYPE_CONNECTION_PROPS }; #define Z_SZIG_MAX_PROPS 16 typedef gint ZSzigEvent; typedef struct _ZSzigValue ZSzigValue; typedef struct _ZSzigNode ZSzigNode; typedef struct _ZSzigProps ZSzigProps; typedef struct _ZSzigServiceProps ZSzigServiceProps; /* * NOTE: this could be represented as a nested structure of ZSzigProps, * however it is special cased for speed. */ struct _ZSzigServiceProps { gchar *name; gint instance_id; gushort sec_conn_id; gushort related_id; gint string_count; gchar *string_list[Z_SZIG_MAX_PROPS * 2]; }; struct _ZSzigProps { gchar *name; gint value_count; gchar *name_list[Z_SZIG_MAX_PROPS]; ZSzigValue *value_list[Z_SZIG_MAX_PROPS]; }; struct _ZSzigValue { gint type; union { glong long_value; GTimeVal time_value; GString *string_value; ZSzigProps props_value; ZSzigServiceProps service_props; } u; }; /** * ZSzigNode: * * A node in the result tree. **/ struct _ZSzigNode { gchar *name; ZSzigValue value; gpointer agr_data; GDestroyNotify agr_notify; GPtrArray *children; }; typedef void (*ZSzigEventHandler)(ZSzigNode *node, ZSzigEvent ev, ZSzigValue *param, gpointer user_data); void z_szig_event(ZSzigEvent ev, ZSzigValue *param); void z_szig_init(const gchar *instance_name); ZSzigValue *z_szig_value_new_long(glong val); ZSzigValue *z_szig_value_new_time(GTimeVal *val); ZSzigValue *z_szig_value_new_string(const gchar *val); void z_szig_value_add_connection_prop(ZSzigValue *v, const gchar *name, const gchar *value); ZSzigValue *z_szig_value_new_connection_props(const gchar *service, gint instance_id, gushort sec_conn_id, gushort related_id, const gchar *name, ...); void z_szig_value_add_prop(ZSzigValue *v, const gchar *name, ZSzigValue *value); ZSzigValue *z_szig_value_new_props(const gchar *name, const gchar *first_prop, ...); void z_szig_value_free(ZSzigValue *v, gboolean free_inst); ZSzigNode *z_szig_tree_lookup(const gchar *node_name, gboolean create, ZSzigNode **parent, gint *parent_ndx); #endif zorp-3.9.5/lib/zorp/timestamp.h000066400000000000000000000001151172670260400164400ustar00rootroot00000000000000#ifndef ZORP_TIMESTAMP_H_INCLUDED #define ZORP_TIMESTAMP_H_INCLUDED #endif zorp-3.9.5/lib/zorp/tpsocket.h000066400000000000000000000007061172670260400162770ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_TPROXY_H_INCLUDED #define ZORP_TPROXY_H_INCLUDED #include gboolean z_tp_socket_init(void); #endif zorp-3.9.5/lib/zorp/zasauth.h000066400000000000000000000003701172670260400161170ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * ***************************************************************************/ zorp-3.9.5/lib/zorp/zorp.h000066400000000000000000000064051172670260400154370ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_H_INCLUDED #define ZORP_H_INCLUDED #ifdef _XOPEN_SOURCE #undef _XOPEN_SOURCE #endif #ifdef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif #include #include #include #include #include #include "zorpconfig.h" #define ZORP_POLICY_FILE ZORP_SYSCONFDIR "/policy.py" #define ZORP_POLICY_BOOT_FILE ZORP_DATADIR "/policy.boot" #define ZORP_AUTH_CERT_FILE ZORP_SYSCONFDIR "/zorp.crt" #define ZORP_AUTH_KEY_FILE ZORP_SYSCONFDIR "/zorp.key" #define ZORP_STATE_DIR ZORP_STATEDIR #define ZORP_PID_FILE_DIR ZORP_PIDFILEDIR #define ZORP_SZIG_SOCKET_NAME ZORP_PID_FILE_DIR "zorpctl" #define ZORP_WORKING_DIR ZORP_STATEDIR "/cores" #define MAX_SESSION_ID 128 #define DEADLOCK_CHECKER_DEFAULT_TIMEOUT 60 #define CORE_POLICY "core.policy" #define CORE_AUDIT "core.audit" #define CORE_VIOLATION "core.violation" #if SIZEOF_VOID_P == 4 # define G_GPOINTER_FORMAT "08" G_GSIZE_MODIFIER "x" #elif SIZEOF_VOID_P == 8 # define G_GPOINTER_FORMAT "016" G_GSIZE_MODIFIER "x" #else # error "Can't find suitable printf format for pointers" #endif extern GMainLoop *main_loop; extern gint exit_code; extern gboolean usr1_received; extern gboolean usr2_received; extern guint32 startup_id; extern const gchar *instance_name; extern const gchar *virtual_instance_name; extern gboolean zorp_process_master_mode; void z_main_loop_initiate_reload(gboolean called_from_sighandler); gboolean z_main_loop_get_last_reload_result(void); void z_main_loop_initiate_termination(gboolean called_from_sighandler); void z_main_loop(const gchar *policy_file, const gchar *instance_name, gchar const **instance_policy_list, gchar const *virtual_instance_name, gboolean is_master); void z_main_loop_quit(int exit_code); void z_main_loop_init(void); void z_main_loop_destroy(void); void z_log_set_fake_session_id(const gchar *instance_name G_GNUC_UNUSED); #if GLIB_MINOR_VERSION < 8 # define G_GNUC_NULL_TERMINATED #endif #if GLIB_MINOR_VERSION < 10 # define G_GNUC_WARN_UNUSED_RESULT #endif #endif zorp-3.9.5/lib/zorp/zorpconfig.h.in000066400000000000000000000074021172670260400172300ustar00rootroot00000000000000/* lib/zorp/zorpconfig.h.in. Generated from configure.in by autoheader. */ /* Zorp brochure version */ #undef BROCHURE_VERSION /* Enable debugging */ #undef ENABLE_DEBUG /* Enable IP option processing */ #undef ENABLE_IPOPTIONS /* Enable IPv6 support */ #undef ENABLE_IPV6 /* Enable prefork support */ #undef ENABLE_PREFORK /* Enable trace messages */ #undef ENABLE_TRACE /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if you have the `gethostbyname_r' function. */ #undef HAVE_GETHOSTBYNAME_R /* Define to 1 if you have the header file. */ #undef HAVE_GOOGLE_COREDUMPER_H /* Define to 1 if you have the `inet_aton' function. */ #undef HAVE_INET_ATON /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Have MSG_PROXY flag (Linux 2.2) */ #undef HAVE_MSG_PROXY /* Define to 1 if you have the `prctl' function. */ #undef HAVE_PRCTL /* Zorp may enable core_dumping Linux 2.4- */ #undef HAVE_PR_SET_DUMPABLE /* Define to 1 if you have the header file. */ #undef HAVE_PYTHON_H /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF /* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* We have a Solaris style gethostbyname_r; */ #undef HAVE_SUN_GETHOSTBYNAME_R /* Define to 1 if you have the header file. */ #undef HAVE_SYS_CAPABILITY_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PRCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF /* Define to the sub-directory in which libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* The size of `void *', as computed by sizeof. */ #undef SIZEOF_VOID_P /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Zorp package version */ #undef VERSION /* Configuration date */ #undef ZORP_CONFIG_DATE /* datadir */ #undef ZORP_DATADIR /* libdir */ #undef ZORP_LIBDIR /* Required license version */ #undef ZORP_LICENSE_VERSION /* pidfiledir */ #undef ZORP_PIDFILEDIR /* Required product name in license */ #undef ZORP_PRODUCT_NAME /* Zorp source revision number */ #undef ZORP_SOURCE_REVISION /* localstatedir */ #undef ZORP_STATEDIR /* sysconfdir */ #undef ZORP_SYSCONFDIR /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES zorp-3.9.5/lib/zorp/zpython.h000066400000000000000000000031051172670260400161520ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_ZPYTHON_H_INCLUDED #define ZORP_ZPYTHON_H_INCLUDED #include #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5 # define Z_PYMAPPING_LENFUNC_TYPE lenfunc # define Z_PYTHON_SIZE_TYPE gssize #else # define Z_PYMAPPING_LENFUNC_TYPE inquiry # define Z_PYTHON_SIZE_TYPE int #endif gboolean z_python_init(void); gboolean z_python_destroy(void); void z_python_lock(void); void z_python_unlock(void); #endif zorp-3.9.5/lib/zpython.c000066400000000000000000000054371172670260400151650ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : Bazsi * Auditor : * Last audited version: * Notes: * ***************************************************************************/ #include static PyThreadState *initial_thread; /** * z_python_init: * * Initialize the low level Python-Zorp interface. Called by the Python * policy implementation. * * Returns: TRUE if initialization was successful **/ gboolean z_python_init(void) { char buf[2048]; if (getenv("PYTHONPATH") == NULL) { g_snprintf(buf, sizeof(buf), "PYTHONPATH=%s", ZORP_DATADIR "/pylib"); } else { g_snprintf(buf, sizeof(buf), "PYTHONPATH=%s:%s", ZORP_DATADIR "/pylib", getenv("PYTHONPATH")); } putenv(buf); PySys_AddWarnOption("ignore:hex/oct constants > sys.maxint will return positive values in Python 2.4 and up:FutureWarning"); PySys_AddWarnOption("ignore:x< * Auditor: * Last audited version: * Notes: * This is a generalized ZTransfer derived class which can be used to * transfer dot terminated data streams like in the POP3 and SMTP protocols. * It assumes that the client side is ZStreamLine compatible. * ***************************************************************************/ #include #include #include #include /* ZDotTransfer implementation */ extern ZClass ZDotTransfer__class; /** * z_dot_transfer_src_read: * @s: ZDotTransfer instance * @stream: stream to read data from * @buf: buffer to store data into * @count: number of bytes to read * @bytes_read: the number of actually read bytes * @err: error details * * This function is registered as the virtual src_read() function for * ZDotTransfer. It effectively reads lines from its input, and indicates EOF * when a '.' is encountered in line on its own. **/ static GIOStatus z_dot_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize count, gsize *bytes_read, GError **err) { ZDotTransfer *self = Z_CAST(s, ZDotTransfer); GError *local_error = NULL; GIOStatus res; gsize read_len; z_proxy_enter(self->super.owner); *bytes_read = 0; if (count < 2) { z_proxy_leave(self->super.owner); return G_IO_STATUS_AGAIN; } read_len = count - 2; res = z_stream_line_get_copy(stream, buf, &read_len, &local_error); switch (res) { case G_IO_STATUS_NORMAL: if (!self->previous_line_split && read_len > 0 && buf[0] == '.') { if (read_len == 1) { res = G_IO_STATUS_EOF; break; } else { memmove(buf, &buf[1], read_len - 1); read_len = read_len - 1; } } buf[read_len] = '\r'; buf[read_len + 1] = '\n'; *bytes_read = read_len + 2; self->previous_line_split = FALSE; break; case G_IO_STATUS_AGAIN: *bytes_read = read_len; if (read_len > 0) { self->previous_line_split = TRUE; res = G_IO_STATUS_NORMAL; } break; case G_IO_STATUS_EOF: /*LOG This message indicates that server unexpectedly closed its connection. */ z_log(NULL, CORE_ERROR, 4, "Unexpected EOF while transferring from server;"); res = G_IO_STATUS_ERROR; break; case G_IO_STATUS_ERROR: res = G_IO_STATUS_ERROR; } if (local_error) g_propagate_error(err, local_error); z_proxy_leave(self->super.owner); return res; } /** * z_dot_transfer_dst_write_preamble: * @self: ZDotTransfer instance * @stream: stream to write to * @err: error details * * This function is called to flush the preamble right before the first byte to the * target stream would be written. Using a preamble makes it possible to conditionally * prefix the data stream with some protocol information, which is only needed when * our child proxy actually sent us something. **/ static GIOStatus z_dot_transfer_dst_write_preamble(ZDotTransfer *self, ZStream *stream, GError **err) { GIOStatus res = G_IO_STATUS_NORMAL; GError *local_error = NULL; gsize bw; z_proxy_enter(self->super.owner); res = z_stream_write(stream, &self->preamble->str[self->preamble_ofs], self->preamble->len - self->preamble_ofs, &bw, &local_error); if (res == G_IO_STATUS_NORMAL) { self->preamble_ofs += bw; if (self->preamble_ofs != self->preamble->len) res = G_IO_STATUS_AGAIN; } if (local_error) g_propagate_error(err, local_error); z_proxy_leave(self->super.owner); return res; } /** * z_dot_transfer_dst_write: * @s: ZDotTransfer passed as a ZTransfer2 * @stream: stream to write data into * @buf: data to write * @count: size of data in @buf to write * @bytes_written: number of bytes actually written * @err: error details * * This function is registered as the virtual dst_write() function of * ZTransfer2. It basically send the contents of @buf to @stream, escaping * '.' characters when encountered as the first character in a line as it * goes. **/ static GIOStatus z_dot_transfer_dst_write(ZTransfer2 *s, ZStream *stream, const gchar *buf, gsize count, gsize *bytes_written, GError **err) { ZDotTransfer *self = Z_CAST(s, ZDotTransfer); GError *local_error = NULL; GIOStatus res = G_IO_STATUS_NORMAL; gsize i, bw; z_proxy_enter(self->super.owner); *bytes_written = 0; switch (self->dst_write_state) { case DOT_DW_PREAMBLE: res = z_dot_transfer_dst_write_preamble(self, stream, &local_error); if (res != G_IO_STATUS_NORMAL) { break; } self->dst_write_state = DOT_DW_DATA_LF; /* fallthrough */ data_state: case DOT_DW_DATA: case DOT_DW_DATA_LF: for (i = *bytes_written; i < count; i++) { if (self->dst_write_state == DOT_DW_DATA) { if (buf[i] == '\n') { self->dst_write_state = DOT_DW_DATA_LF; } } else if (self->dst_write_state == DOT_DW_DATA_LF) { if (buf[i] == '.') { /* we need to escape this '.' */ /* first, write buf up to this '.' */ res = z_stream_write(stream, buf + *bytes_written, i - *bytes_written, &bw, &local_error); if (res == G_IO_STATUS_NORMAL && i == bw) { *bytes_written += bw; self->dst_write_state = DOT_DW_DATA_DOT; goto dot_state; } else { /* we wrote less bytes, go back to the original state */ self->dst_write_state = DOT_DW_DATA; *bytes_written += bw; break; } } self->dst_write_state = DOT_DW_DATA; } } if (i == count) { /* no need to escape */ res = z_stream_write(stream, buf + *bytes_written, count - *bytes_written, &bw, &local_error); *bytes_written += bw; } break; dot_state: case DOT_DW_DATA_DOT: res = z_stream_write(stream, ".", 1, &bw, &local_error); if (res == G_IO_STATUS_NORMAL && bw == 1) { self->dst_write_state = DOT_DW_DATA; goto data_state; } break; } if (local_error) g_propagate_error(err, local_error); z_proxy_leave(self->super.owner); return res; } /** * z_dot_transfer_dst_shutdown: * @s: ZDotTransfer passed as a ZTransfer2 * @stream: stream to shut down * @err: error details * * This function is registered as the virtual dst_shutdown() function for * ZTransfer2. It basically checks whether the transfer actually terminated the * data stream by CRLF, and terminates it that was not the case. This is required * by for example mail transfers when mail bodies must end in CRLF in order * to correctly interpret the closing '.'. **/ static GIOStatus z_dot_transfer_dst_shutdown(ZTransfer2 *s, ZStream *stream, GError **err) { ZDotTransfer *self = Z_CAST(s, ZDotTransfer); GIOStatus res = G_IO_STATUS_NORMAL; /* NOTE: this code ensures that our output to the server side is * terminated by a CRLF before the last '.' is written. */ if (self->dst_write_state == DOT_DW_DATA && ((self->super.status & (ZT2S_FAILED+ZT2S_TIMEDOUT+ZT2S_ABORTED)) == 0)) { gsize bw; res = z_stream_write(stream, "\r\n", 2, &bw, err); } return res; } /** * z_dot_transfer_new: * @class: class to instantiate * @owner: owner proxy * @poll: ZPoll instance * @client: client stream * @server: server stream * @buffer_size: buffer size * @timeout: inactivity timeout * @flags: bit combination of ZT2F_* * @preamble: string to send before the first byte is actually written to the destination * * This function is the constructor for ZDotTransfer a '.' terminated bulk transfer method, * used by POP3 for example. (or NNTP, or SMTP) * SMTP does not use this one, as it needs to send NOOPs to the server while transferring. **/ ZDotTransfer * z_dot_transfer_new(ZClass *class, ZProxy *owner, ZPoll *poll, ZStream *client, ZStream *server, gsize buffer_size, glong timeout, gulong flags, GString *preamble) { ZDotTransfer *self; z_proxy_enter(owner); self = Z_CAST(z_transfer2_new(class, owner, poll, client, server, buffer_size, timeout, ZT2F_COMPLETE_COPY | flags), ZDotTransfer); self->preamble = preamble; z_proxy_leave(owner); return self; } /** * z_dot_transfer_free_method: * @s: ZDotTransfer passed as a ZObject * * Destructor function for ZDotTransfer instances, frees self->preamble and * calls the inherited free method. **/ static void z_dot_transfer_free_method(ZObject *s) { ZDotTransfer *self = Z_CAST(s, ZDotTransfer); g_string_free(self->preamble, TRUE); z_transfer2_free_method(s); } ZTransfer2Funcs z_dot_transfer_funcs = { { Z_FUNCS_COUNT(ZTransfer2), z_dot_transfer_free_method, }, z_dot_transfer_src_read, z_dot_transfer_dst_write, NULL, /* src_shutdown */ z_dot_transfer_dst_shutdown, NULL, /* stack_proxy */ NULL, /* setup */ NULL, NULL /* progress */ }; Z_CLASS_DEF(ZDotTransfer, ZTransfer2, z_dot_transfer_funcs); zorp-3.9.5/libproxy/errorloader.c000066400000000000000000000146711172670260400170740ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * Author: SZALAY Attila * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include void z_error_append_escaped(GString *content, const gchar *append, guint32 flags) { const gchar *p; g_assert((flags & (Z_EF_ESCAPE_NONE + Z_EF_ESCAPE_HTML)) != 0); if (flags & Z_EF_ESCAPE_NONE) { g_string_append(content, append); return; } for (p = append; *p; p++) { if (flags & Z_EF_ESCAPE_HTML) { if (*p == '<') g_string_append(content, "<"); else if (*p == '>') g_string_append(content, ">"); else if (*p == '"') g_string_append(content, """); else if (*p == '&') g_string_append(content, "&"); else g_string_append_c(content, *p); } } } gchar * z_error_loader_format_file(gchar *filepath, gchar *additional_info, guint32 flags, ZErrorLoaderVarInfo *infos, gpointer user_data) { gint fd; GString *new_contents = NULL; gchar *ret = NULL; z_enter(); fd = open(filepath, O_RDONLY); if (fd == -1) { /*LOG This message indicates that Zorp was unable to open the error file for the given reason. It is likely that the file does not exist, or has too restrictive permissions. */ z_log(NULL, CORE_ERROR, 3, "I/O error opening error file; filename='%s', error='%s'", filepath, g_strerror(errno)); goto exit; } else { gchar contents[4096], *src; gint count; new_contents = g_string_sized_new(4096); count = read(fd, contents, sizeof(contents) - 1); while (count > 0) { contents[count] = 0; src = contents; while (*src) { if (*src == '@') { if (strncmp(src, "@INFO@", 6) == 0) { src += 5; z_error_append_escaped(new_contents, additional_info, flags); } else if (strncmp(src, "@VERSION@", 9) == 0) { src += 8; z_error_append_escaped(new_contents, VERSION, flags); } else if (strncmp(src, "@DATE@", 6) == 0) { time_t t; gchar timebuf[64]; struct tm tm; src += 5; t = time(NULL); localtime_r(&t, &tm); strftime(timebuf, sizeof(timebuf), "%a %b %e %H:%M:%S %Z %Y", &tm); z_error_append_escaped(new_contents, timebuf, flags); } else if (strncmp(src, "@HOST@", 6) == 0) { gchar hostname[256]; src += 5; if (gethostname(hostname, sizeof(hostname)) == 0) z_error_append_escaped(new_contents, hostname, flags); } else { gint i = 0; if (infos) { gint left = strlen(src + 1); for (i = 0; infos[i].variable != NULL; i++) { gint var_length = strlen(infos[i].variable); if (left > var_length && strncmp(src + 1, infos[i].variable, strlen(infos[i].variable)) == 0 && src[var_length + 1] == '@') { gchar *info; info = infos[i].resolve(infos[i].variable, user_data); if (info) { z_trace(NULL, "Replace info stub; type='%s', data='%s'", infos[i].variable, info); z_error_append_escaped(new_contents, info, flags); g_free(info); } break; } } if (infos[i].variable != NULL) src += strlen(infos[i].variable) + 1; } if (infos == NULL || infos[i].variable == NULL) { z_cp(); g_string_append_c(new_contents, *src); } } } else { g_string_append_c(new_contents, *src); } src++; } count = read(fd, contents, sizeof(contents) - 1); } close(fd); if (count < 0) { g_string_free(new_contents, TRUE); new_contents = NULL; } } exit: if (new_contents) ret = g_string_free(new_contents, FALSE); z_return(ret); } zorp-3.9.5/libproxy/polling_proxy.c000066400000000000000000000000001172670260400174360ustar00rootroot00000000000000zorp-3.9.5/libproxy/transfer2.c000066400000000000000000001026421172670260400164560ustar00rootroot00000000000000#include #include #include #define MAX_READ_AT_A_TIME 30 typedef struct _ZTransfer2PSIface { ZProxyStackIface super; ZTransfer2 *transfer; } ZTransfer2PSIface; extern ZClass ZTransfer2PSIface__class; static inline void z_transfer2_update_status(ZTransfer2 *self, guint32 status_bit, gint enable) { guint32 old_mask = self->status & ZT2S_EOF_BITS; if (enable) self->status |= status_bit; else self->status &= ~status_bit; /*LOG This message reports that the data-transfer to or from some endpoint is closed. */ z_proxy_log(self->owner, CORE_DEBUG, 7, "Eofmask is updated; old_mask='%04x', eof_mask='%04x'", old_mask, self->status & ZT2S_EOF_BITS); } /** * z_transfer2_ps_iface_set_stacked_verdict: * @self: ZProxyStackIface instance * @verdict: verdict sent by the child proxy * @description: additional information about @decision * * This function is the virtual set_result method in the ZProxyStackIface * interface callable by child proxies. It simply stores the verdict sent * by the child proxy in self->stack_decision. * * NOTE: this function runs in the thread of the child proxy and there is no * synchronization between this and the main proxy thread except for the fact * that the child proxy should call this function before he sends anything * and HTTP will make use of this value when it first receives something, * thus there must be at least one context switch in between, and the HTTP * is possibly sleeping when this function is called. **/ static void z_transfer2_ps_iface_set_stacked_verdict(ZProxyStackIface *s, ZVerdict verdict, const gchar *description) { ZTransfer2PSIface *self = Z_CAST(s, ZTransfer2PSIface); g_string_assign(self->transfer->stack_info, description ? description : ""); self->transfer->stack_decision = verdict; } /** * http_set_content_length: * @self: ZProxyStackIface instance * @content_length: the length of the content to be transferred * * This function is the virtual set_content_length method in the * ZProxyStackIface interface callable by child proxies. It simply stores * the future size of the transferred data blob. This information will then * be used by the transfer code to decide whether to include a * content-length header. Calling this function is not mandatory by the * child proxy, if it is not called chunked mode transfer-encoding will be * used. */ static void z_transfer2_ps_iface_set_content_hint(ZProxyStackIface *s, gint64 content_length) { ZTransfer2PSIface *self = Z_CAST(s, ZTransfer2PSIface); self->transfer->child_content_length_hint = content_length; self->transfer->child_content_length_hint_set = TRUE; } /** * http_get_content_hint: * @self: ZProxyStackIface instance * @content_length: the length of the content to be transferred * * This function is the virtual get_content_length method in the * ZProxyStackIface interface callable by child proxies. */ static gboolean z_transfer2_ps_iface_get_content_hint(ZProxyStackIface *s, gint64 *content_length, const gchar **content_format) { ZTransfer2PSIface *self = Z_CAST(s, ZTransfer2PSIface); /* Note: Save it with a mutex because it's only be set in z_transfer2_setup in * other thread than this called. */ g_mutex_lock(self->transfer->startup_lock); *content_format = self->transfer->content_format; if (self->transfer->our_content_length_hint_set) *content_length = self->transfer->our_content_length_hint; else *content_length = -1; g_mutex_unlock(self->transfer->startup_lock); return TRUE; } static ZProxyIface * z_transfer2_ps_iface_new(ZTransfer2 *transfer) { ZTransfer2PSIface *self; self = Z_CAST(z_proxy_stack_iface_new(Z_CLASS(ZTransfer2PSIface), transfer->owner), ZTransfer2PSIface); self->transfer = transfer; return &self->super; } static void z_transfer2_ps_iface_free(ZObject *s) { z_proxy_stack_iface_free_method(s); } ZProxyStackIfaceFuncs z_transfer2_ps_iface_funcs = { { Z_FUNCS_COUNT(ZProxyStackIface), .free_fn = z_transfer2_ps_iface_free, }, .set_verdict = z_transfer2_ps_iface_set_stacked_verdict, .set_content_hint = z_transfer2_ps_iface_set_content_hint, .get_content_hint = z_transfer2_ps_iface_get_content_hint }; Z_CLASS_DEF(ZTransfer2PSIface, ZProxyStackIface, z_transfer2_ps_iface_funcs); /** * z_transfer2_buffer_empty: * @self: ZTransfer2Buffer instance * * This function returns TRUE when the buffer specified by @self contains no * data. **/ static inline gboolean z_transfer2_buffer_empty(ZTransfer2Buffer *self) { return self->ofs == self->end; } /** * z_transfer2_buffer_full: * @self: ZTransfer2Buffer instance * * This function returns TRUE when the buffer specified by @self is full **/ static inline gboolean z_transfer2_buffer_full(ZTransfer2Buffer *self) { return self->end == self->size; } /** * z_transfer2_buffer_init: * @self: ZTransfer2Buffer instance * @buffer_size: buffer size * * This function initializes a ZTransfer2Buffer structure and allocates the * memory area where the buffer is stored. * * NOTE: the ZTransfer2Buffer structure itself is not allocated, it is * expected that it is a member of some container structure, thus it will be * allocated independently by the caller. **/ static inline void z_transfer2_buffer_init(ZTransfer2Buffer *self, gsize buffer_size) { self->buf = g_malloc(buffer_size); self->size = buffer_size; } /** * z_transfer2_buffer_destroy: * @self: ZTransfer2Buffer instance * * This function frees the memory area associated to the buffer of * the ZTransfer2Buffer structure specified by @self. * * NOTE: the ZTransfer2Buffer structure itself is not freed, it is * expected that it is a member of some container structure, thus it will be * freed independently by the caller. **/ static inline void z_transfer2_buffer_destroy(ZTransfer2Buffer *self) { g_free(self->buf); } /** * z_transfer2_timeout: * @user_data: ZTransfer2 instance passed as a generic pointer * * This function is a timeout-callback registered to terminate the * transfer loop when a specified time elapses. **/ static gboolean z_transfer2_timeout(gpointer user_data) { ZTransfer2 *self = Z_CAST(user_data, ZTransfer2); z_proxy_enter(self->owner); /*LOG This message indicates the data transfer timed out. */ z_proxy_log(self->owner, CORE_ERROR, 3, "Data transfer timed out; timeout='%ld'", self->timeout); z_transfer2_update_status(self, ZT2S_TIMEDOUT+ZT2S_FAILED+ZT2S_FINISHED, TRUE); z_proxy_leave(self->owner); return FALSE; } /** * z_transfer2_timed_progress: * @user_data: ZTransfer2 instance passed as a generic pointer * * This function is the timeout callback registered to be called when the * interval specified by the timeout_progress variable elapses. This is * used for example to generate NOOPs in SMTP while transfer is downloading * data to the virus checking proxy. **/ static gboolean z_transfer2_timed_progress(gpointer user_data) { ZTransfer2 *self = Z_CAST(user_data, ZTransfer2); z_proxy_enter(self->owner); if (!z_transfer2_progress(self)) { /*LOG This message indicates that the data-transfer is interrupted by a timed progress callback and Zorp is closing the date-transfer channels. */ z_proxy_log(self->owner, CORE_ERROR, 3, "Data transfer interrupted by progress;"); z_transfer2_update_status(self, ZT2S_FAILED+ZT2S_FINISHED, TRUE); } z_timeout_source_set_timeout(self->progress_source, self->progress_interval); z_proxy_leave(self->owner); return TRUE; } /** * z_transfer2_update_cond: * @self: ZTransfer2 instance * * This function is called after a chunk of data was transferred to update * the stream read/write conditions. It works based on the bits * stored in self->status. **/ static void z_transfer2_update_cond(ZTransfer2 *self) { gint i; z_proxy_enter(self->owner); for (i = 0; i <= ZT2E_MAX; i++) { if ((i & ZT2E_STACKED) == 0 || self->stacked) { z_stream_set_cond(z_transfer2_get_stream(self, i), G_IO_IN, FALSE); z_stream_set_cond(z_transfer2_get_stream(self, i), G_IO_OUT, FALSE); } } if (self->stacked) { if (!z_transfer2_get_status(self, ZT2S_EOF_SOURCE)) { 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); } if (!z_transfer2_get_status(self, ZT2S_EOF_DEST)) { if (z_transfer2_buffer_empty(&self->buffers[1])) z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), G_IO_IN, TRUE); else z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_DEST), G_IO_OUT, TRUE); } } else { /* 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) && !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); } } z_proxy_leave(self->owner); } /** * z_transfer2_eof: * @self: ZTransfer2 instance * * This function is called when transfer encounters an EOF one of the * endpoints. It updates self->status end also terminates the poll loop * by setting ZT2S_FINISHED when the transfer supposedly finished. **/ static void z_transfer2_eof(ZTransfer2 *self, gint endpoint) { guint32 eof_status = endpoint == ZT2E_SOURCE ? ZT2S_EOF_SOURCE : ZT2S_EOF_DEST; z_proxy_enter(self->owner); if (!z_transfer2_get_status(self, eof_status)) { if (self->stacked) { if (endpoint == ZT2E_SOURCE) { z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_SOURCE), FALSE); z_transfer2_src_shutdown(self, z_transfer2_get_stream(self, ZT2E_SOURCE), NULL); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_SOURCE), TRUE); z_stream_shutdown(z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE), SHUT_WR, NULL); } else { z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), FALSE); z_stream_shutdown(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), SHUT_RD, NULL); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), TRUE); z_transfer2_dst_shutdown(self, z_transfer2_get_stream(self, ZT2E_DEST), NULL); } } else { z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_SOURCE), FALSE); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DEST), FALSE); z_transfer2_src_shutdown(self, z_transfer2_get_stream(self, ZT2E_SOURCE), NULL); z_transfer2_dst_shutdown(self, z_transfer2_get_stream(self, ZT2E_DEST), NULL); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DEST), TRUE); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_SOURCE), TRUE); eof_status = ZT2S_EOF_SOURCE+ZT2S_EOF_DEST; } z_transfer2_update_status(self, eof_status, TRUE); } if ((self->status & (ZT2S_EOF_SOURCE+ZT2S_EOF_DEST)) == (ZT2S_EOF_SOURCE+ZT2S_EOF_DEST)) { z_transfer2_update_status(self, ZT2S_FINISHED, TRUE); } z_proxy_leave(self->owner); } /** * z_transfer2_read_source: * @self: ZTransfer2 instance * @endpoint: endpoint to fetch data from * @buf: store fetched information into this buffer * @error: error details are stored here * * This function is called to fetch data from the specified endpoint. When * it is a proxy-connected stream then the proxy provided callbacks are * used to fetch information, otherwise z_stream_read is called directly. **/ static GIOStatus z_transfer2_read_source(ZTransfer2 *self, gint endpoint, ZTransfer2Buffer *buf, GError **error) { ZStream *from = z_transfer2_get_stream(self, endpoint); GIOStatus res = G_IO_STATUS_NORMAL; GError *local_error = NULL; gsize read_len; z_proxy_enter(self->owner); if (endpoint & ZT2E_STACKED) { res = z_stream_read(from, &buf->buf[buf->end], buf->size - buf->end, &read_len, &local_error); } else if (endpoint == ZT2E_SOURCE) { res = z_transfer2_src_read(self, self->endpoints[endpoint], &buf->buf[buf->end], buf->size - buf->end, &read_len, &local_error); } if (res == G_IO_STATUS_NORMAL) { buf->end += read_len; } if (local_error) g_propagate_error(error, local_error); z_proxy_leave(self->owner); return res; } /** * z_transfer2_write_dest: * @self: ZTransfer2 instance * @endpoint: endpoint to send data to * @buf: send data from this buffer * @error: error details are stored here * * This function is called to send data to the specified endpoint. When * it is a proxy-connected stream then the proxy provided callbacks are * used to send information, otherwise z_stream_write is called directly. **/ static GIOStatus z_transfer2_write_dest(ZTransfer2 *self, gint endpoint, ZTransfer2Buffer *buf, GError **error) { ZStream *to = z_transfer2_get_stream(self, endpoint); GError *local_error = NULL; GIOStatus res = G_IO_STATUS_NORMAL; gsize bytes_written; z_proxy_enter(self->owner); if (!z_transfer2_buffer_empty(buf)) { if (endpoint & ZT2E_STACKED) res = z_stream_write(to, &buf->buf[buf->ofs], buf->end - buf->ofs, &bytes_written, &local_error); else res = z_transfer2_dst_write(self, to, &buf->buf[buf->ofs], buf->end - buf->ofs, &bytes_written, &local_error); switch (res) { case G_IO_STATUS_NORMAL: buf->ofs += bytes_written; if (!z_transfer2_buffer_empty(buf)) { res = G_IO_STATUS_AGAIN; } default: break; } } if (local_error) g_propagate_error(error, local_error); z_proxy_leave(self->owner); return res; } /** * z_transfer2_copy_data: * @self: ZTransfer2 instance * @ep_from: source endpoint * @ep_to: destination endpoint * @error: error details are stored here * * This function is the central copy-loop of ZTransfer2 and is called by I/O * callbacks assigned to various streams. It copies data while: * 1) data is available (e.g. G_IO_STATUS_NORMAL is returned) * 2) have not copied MAX_READ_AT_A_TIME chunks yet * 3) data can be flushed to destination (e.g. G_IO_STATUS_NORMAL is returned) * * when any of the conditions become FALSE, z_transfer2_copy_data returns, * but the operation can simply be restarted by calling it again. * Information stored in internal buffers are automatically reused in the * next invocation. * * This function also updates the timeout timer to indicate that some I/O * has happened, thus the timeout callback does not need to be called. **/ static GIOStatus z_transfer2_copy_data(ZTransfer2 *self, gint ep_from, gint ep_to, GError **error) { GError *local_error = NULL; ZTransfer2Buffer *buf = &self->buffers[ep_from & ~ZT2E_STACKED]; gint pkt_count = 0; GIOStatus res = G_IO_STATUS_NORMAL; gboolean leave_while = FALSE; z_proxy_enter(self->owner); if (self->timeout_source) z_timeout_source_set_timeout(self->timeout_source, self->timeout); while (pkt_count < MAX_READ_AT_A_TIME && !leave_while) { if (!z_transfer2_get_status(self, ZT2S_COPYING_TAIL)) { res = z_transfer2_write_dest(self, ep_to, buf, &local_error); if (res == G_IO_STATUS_NORMAL) { if (!z_transfer2_buffer_empty(buf)) break; } else if (res == G_IO_STATUS_AGAIN) { break; } else { z_transfer2_update_status(self, ZT2S_FAILED, TRUE); if (self->flags & ZT2F_COMPLETE_COPY) { z_transfer2_update_status(self, ZT2S_COPYING_TAIL, TRUE); } else { z_transfer2_update_status(self, ZT2S_FINISHED, TRUE); } break; } } else { buf->ofs = buf->end = 0; } if (z_transfer2_buffer_empty(buf)) { buf->ofs = buf->end = 0; } while (pkt_count < MAX_READ_AT_A_TIME && !z_transfer2_buffer_full(buf)) { guint eof_status = ep_from == ZT2E_SOURCE ? ZT2S_SOFT_EOF_SOURCE : ZT2S_SOFT_EOF_DEST; if (!z_transfer2_get_status(self, eof_status)) { res = z_transfer2_read_source(self, ep_from, buf, &local_error); if (res == G_IO_STATUS_NORMAL) { ; } else if (res == G_IO_STATUS_AGAIN) { leave_while = TRUE; break; } else if (res == G_IO_STATUS_EOF) { if (z_transfer2_buffer_empty(buf)) { z_transfer2_eof(self, ep_from); leave_while = TRUE; break; } else { z_transfer2_update_status(self, eof_status, TRUE); break; } } else { z_transfer2_update_status(self, ZT2S_FINISHED + ZT2S_ABORTED, TRUE); z_transfer2_eof(self, ep_from); leave_while = TRUE; break; } pkt_count++; } else { if (z_transfer2_buffer_empty(buf)) { z_transfer2_eof(self, ep_from); leave_while = TRUE; } break; } } } z_transfer2_update_cond(self); if (local_error) g_propagate_error(error, local_error); z_proxy_leave(self->owner); return res; } /** * z_transfer2_copy_src_to_dst: * @s: ZStream instance * @cond: condition which triggered this callback * @user_data: ZTransfer2 instance passed as a generic pointer * * This function is registered as the "readable" callback of the client-side * stream when no stacking is used, thus data is directly copied to the * server-side. **/ static gboolean z_transfer2_copy_src_to_dst(ZStream *s G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { ZTransfer2 *self = Z_CAST(user_data, ZTransfer2); z_proxy_enter(self->owner); z_transfer2_copy_data(self, ZT2E_SOURCE, ZT2E_DEST, NULL); z_proxy_leave(self->owner); return TRUE; } /** * z_transfer2_copy_src_to_down: * @s: ZStream instance * @cond: condition which triggered this callback * @user_data: ZTransfer2 instance passed as a generic pointer * * This function is registered as the "readable" callback of the client-side * stream when stacking is used, thus data must be copied to the stacked * proxy first. **/ static gboolean z_transfer2_copy_src_to_down(ZStream *s G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { ZTransfer2 *self = Z_CAST(user_data, ZTransfer2); z_proxy_enter(self->owner); z_transfer2_copy_data(self, ZT2E_SOURCE, ZT2E_DOWN_SOURCE, NULL); z_proxy_leave(self->owner); return TRUE; } /** * z_transfer2_copy_down_to_dst: * @s: ZStream instance * @cond: condition which triggered this callback * @user_data: ZTransfer2 instance passed as a generic pointer * * This function is registered as the "readable" callback of the server-side * stacked stream, and copies data to the server-side. **/ static gboolean z_transfer2_copy_down_to_dst(ZStream *s G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { ZTransfer2 *self = Z_CAST(user_data, ZTransfer2); z_proxy_enter(self->owner); z_transfer2_copy_data(self, ZT2E_DOWN_DEST, ZT2E_DEST, NULL); z_proxy_leave(self->owner); return TRUE; } /** * z_transfer2_switch_to_transfer_context: * @self: ZTransfer2 instance * * This function switches all related streams to use the transfer context. **/ static void z_transfer2_switch_to_transfer_context(ZTransfer2 *self) { z_stream_save_context(z_transfer2_get_stream(self, ZT2E_SOURCE), &self->proxy_contexts[0]); z_stream_save_context(z_transfer2_get_stream(self, ZT2E_DEST), &self->proxy_contexts[1]); z_stream_restore_context(z_transfer2_get_stream(self, ZT2E_SOURCE), &self->transfer_contexts[0]); z_stream_restore_context(z_transfer2_get_stream(self, ZT2E_DEST), &self->transfer_contexts[1]); } /** * z_transfer2_switch_to_proxy_context: * @self: ZTransfer2 instance * * This function switches all related streams to use the proxy context. **/ static void z_transfer2_switch_to_proxy_context(ZTransfer2 *self) { z_stream_save_context(z_transfer2_get_stream(self, ZT2E_SOURCE), &self->transfer_contexts[0]); z_stream_save_context(z_transfer2_get_stream(self, ZT2E_DEST), &self->transfer_contexts[1]); z_stream_restore_context(z_transfer2_get_stream(self, ZT2E_SOURCE), &self->proxy_contexts[0]); z_stream_restore_context(z_transfer2_get_stream(self, ZT2E_DEST), &self->proxy_contexts[1]); } /** * z_transfer2_start: * @self: ZTransfer2 instance * * This function must be called after the construction of the ZTransfer2 * object to actually register read/write callbacks and various timers. * Without calling this function z_transfer2_run will not do anything. * **/ gboolean z_transfer2_start(ZTransfer2 *self) { gboolean res; ZProxyIface *iface; z_proxy_enter(self->owner); iface = z_transfer2_ps_iface_new(self); z_proxy_add_iface(self->owner, iface); z_object_unref(&iface->super); g_mutex_lock(self->startup_lock); if (!z_transfer2_stack_proxy(self, &self->stacked)) { g_mutex_unlock(self->startup_lock); z_proxy_log(self->owner, CORE_ERROR, 3, "Could not start stacked proxy, rejecting transfer;"); z_proxy_leave(self->owner); return FALSE; } z_transfer2_switch_to_transfer_context(self); /* NOTE: shutdown goes back to blocking mode in which case timeout is significant */ z_stream_set_timeout(z_transfer2_get_stream(self, ZT2E_SOURCE), self->timeout); z_stream_set_timeout(z_transfer2_get_stream(self, ZT2E_DEST), self->timeout); z_transfer2_buffer_init(&self->buffers[0], self->buffer_size); if ((self->flags & ZT2F_PROXY_STREAMS_POLLED) == 0) { z_poll_add_stream(self->poll, z_transfer2_get_stream(self, ZT2E_SOURCE)); z_poll_add_stream(self->poll, z_transfer2_get_stream(self, ZT2E_DEST)); } /* initialize stacked streams */ if (self->stacked) { if ((self->stacked->flags & Z_SPF_HALF_DUPLEX) == 0) { /* shutdown the reverse direction */ z_stream_shutdown(z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE), SHUT_RD, NULL); z_stream_shutdown(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), SHUT_WR, NULL); } z_transfer2_buffer_init(&self->buffers[1], self->buffer_size); z_poll_add_stream(self->poll, z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE)); z_poll_add_stream(self->poll, z_transfer2_get_stream(self, ZT2E_DOWN_DEST)); z_stream_set_callback(z_transfer2_get_stream(self, ZT2E_SOURCE), G_IO_IN, z_transfer2_copy_src_to_down, self, NULL); z_stream_set_callback(z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE), G_IO_OUT, z_transfer2_copy_src_to_down, self, NULL); z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_SOURCE), G_IO_IN, TRUE); z_stream_set_callback(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), G_IO_IN, z_transfer2_copy_down_to_dst, self, NULL); z_stream_set_callback(z_transfer2_get_stream(self, ZT2E_DEST), G_IO_OUT, z_transfer2_copy_down_to_dst, self, NULL); z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_DEST | ZT2E_STACKED), G_IO_IN, TRUE); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE), TRUE); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DOWN_DEST), TRUE); } else { z_stream_set_callback(z_transfer2_get_stream(self, ZT2E_SOURCE), G_IO_IN, z_transfer2_copy_src_to_dst, self, NULL); z_stream_set_callback(z_transfer2_get_stream(self, ZT2E_DEST), G_IO_OUT, z_transfer2_copy_src_to_dst, self, NULL); z_stream_set_cond(z_transfer2_get_stream(self, ZT2E_SOURCE), G_IO_IN, TRUE); } z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_SOURCE), TRUE); z_stream_set_nonblock(z_transfer2_get_stream(self, ZT2E_DEST), TRUE); res = z_transfer2_setup(self); z_transfer2_switch_to_proxy_context(self); g_mutex_unlock(self->startup_lock); if (self->timeout > 0) { GMainContext *context; self->timeout_source = z_timeout_source_new(self->timeout); g_source_set_callback(self->timeout_source, z_transfer2_timeout, self, NULL); context = z_poll_get_context(self->poll); g_source_attach(self->timeout_source, context); } if (self->progress_interval > 0) { GMainContext *context; self->progress_source = z_timeout_source_new(self->progress_interval); g_source_set_callback(self->progress_source, z_transfer2_timed_progress, self, NULL); context = z_poll_get_context(self->poll); g_source_attach(self->progress_source, context); } z_proxy_leave(self->owner); return res; } /** * z_transfer2_suspend: * @self: ZTransfer2 instance * @suspend_reason: information indicating why transfer was suspended * * This function can be used from proxy provided callbacks while transfer is * taking place. It temporarily suspends the data transfer and returns to * the caller. Transfer can be restarted by calling z_transfer2_run again. * * The suspend feature is used for example to generate NOOP commands to the * SMTP server while transfer is processing data. **/ void z_transfer2_suspend(ZTransfer2 *self, gint suspend_reason) { z_proxy_enter(self->owner); z_transfer2_update_status(self, ZT2S_SUSPENDED, TRUE); self->suspend_reason = suspend_reason; z_proxy_leave(self->owner); } /** * z_transfer2_rollback: * @self: ZTransfer2 instance * * This function can be called when transfer was suspended and the data * stream is to be dropped. It basically consumes the incoming data stream * without actually writing it to the server side. * * This function can only be called when the transfer is suspended. **/ gboolean z_transfer2_rollback(ZTransfer2 *self) { z_enter(); if (z_transfer2_get_status(self, ZT2S_STARTED) && !z_transfer2_get_status(self, ZT2S_FINISHED)) { /* roll back transfer state */ z_transfer2_update_status(self, ZT2S_COPYING_TAIL, TRUE); while (z_transfer2_run(self) == ZT2_RESULT_SUSPENDED) ; } z_return(TRUE); } /** * z_transfer2_cancel: * @self: ZTransfer2 instance * * This function abort the transfer. This function can be called from anywhere but * it's only set the state of the transfer. You have to continue running the * transfer untill it's quit. **/ gboolean z_transfer2_cancel(ZTransfer2 *self) { z_enter(); if (!z_transfer2_get_status(self, ZT2S_FINISHED)) z_transfer2_update_status(self, ZT2S_FINISHED + ZT2S_ABORTED, TRUE); z_return(TRUE); } /** * z_transfer2_run_method: * @self: ZTransfer2 instance * * This function actually performs data transfer. **/ static ZTransfer2Result 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); while (((self->status & (ZT2S_FINISHED+ZT2S_SUSPENDED)) == 0) && z_poll_iter_timeout(self->poll, -1)) { if (!z_proxy_loop_iteration(self->owner)) { z_transfer2_update_status(self, ZT2S_ABORTED + ZT2S_FINISHED, TRUE); break; } } z_transfer2_switch_to_proxy_context(self); if (self->status & ZT2S_SUSPENDED) { z_leave(); return ZT2_RESULT_SUSPENDED; } else if (self->status & ZT2S_FAILED) { z_leave(); return ZT2_RESULT_FAILED; } else if (self->status & ZT2S_ABORTED) { z_leave(); return ZT2_RESULT_ABORTED; } else { z_leave(); return ZT2_RESULT_FINISHED; } z_leave(); } /** * z_transfer2_enable_progress: * @self: ZTransfer2 instance * @progress_interval: interval in milliseconds to call the progress timeout callback * * This function must be called before z_transfer2_start() is called to * enable the progress callback. **/ void z_transfer2_enable_progress(ZTransfer2 *self, glong progress_interval) { self->progress_interval = progress_interval; } gboolean z_transfer2_simple_run(ZTransfer2 *self) { ZTransfer2Result tr; gboolean success; if (!z_transfer2_start(self)) return FALSE; do { tr = z_transfer2_run(self); } while (tr == ZT2_RESULT_SUSPENDED); success = (tr != ZT2_RESULT_FAILED) && (tr != ZT2_RESULT_ABORTED); if (tr == ZT2_RESULT_FAILED) z_transfer2_rollback(self); return success; } /** * z_transfer2_new: * @class: class to instantiate * @owner: owner proxy instance * @poll: ZPoll instance * @source: source stream * @dest: destination stream * @buffer_size: size of the transfer buffer * @timeout: after this amount of inactivity, the transfer is ended with failure * @flags: bit combination of the ZT2F_* constants * * This constructor creates a new ZTransfer2 instance with the specified parameters. **/ ZTransfer2 * z_transfer2_new(ZClass *class, ZProxy *owner, ZPoll *poll, ZStream *source, ZStream *dest, gsize buffer_size, glong timeout, guint32 flags) { ZTransfer2 *self; z_proxy_enter(owner); self = Z_NEW_COMPAT(class, ZTransfer2); z_proxy_ref(owner); self->owner = owner; z_poll_ref(poll); self->poll = poll; self->endpoints[0] = z_stream_ref(source); self->endpoints[1] = z_stream_ref(dest); self->buffer_size = buffer_size; self->timeout = timeout; self->flags = flags; self->content_format = "file"; self->startup_lock = g_mutex_new(); self->stack_info = g_string_sized_new(32); self->stack_decision = ZV_ACCEPT; z_proxy_leave(owner); return self; } /** * z_transfer2_free_method: * @s: ZTransfer2 instance passed as a ZObject * * This method is called when a ZTransfer2 instance is freed. It has to free * all dynamically allocated members in @s. **/ void z_transfer2_free_method(ZObject *s) { ZTransfer2 *self = Z_CAST(s, ZTransfer2); ZProxyIface *iface; guint i; z_enter(); iface = z_proxy_find_iface(self->owner, Z_CLASS(ZTransfer2PSIface)); if (iface) { z_proxy_del_iface(self->owner, iface); z_object_unref(&iface->super); } z_proxy_unref(self->owner); if ((self->flags & ZT2F_PROXY_STREAMS_POLLED) == 0) { z_poll_remove_stream(self->poll, z_transfer2_get_stream(self, ZT2E_SOURCE)); z_poll_remove_stream(self->poll, z_transfer2_get_stream(self, ZT2E_DEST)); } z_stream_unref(self->endpoints[0]); z_stream_unref(self->endpoints[1]); z_transfer2_buffer_destroy(&self->buffers[0]); if (self->stacked) { z_poll_remove_stream(self->poll, z_transfer2_get_stream(self, ZT2E_DOWN_SOURCE)); z_poll_remove_stream(self->poll, z_transfer2_get_stream(self, ZT2E_DOWN_DEST)); z_stacked_proxy_destroy(self->stacked); z_transfer2_buffer_destroy(&self->buffers[1]); } if (self->timeout_source) { g_source_destroy(self->timeout_source); g_source_unref(self->timeout_source); self->timeout_source = NULL; } if (self->progress_source) { g_source_destroy(self->progress_source); g_source_unref(self->progress_source); self->progress_source = NULL; } for (i = 0; i < EP_MAX; i++) { if (self->transfer_contexts[i].stream_extra) z_stream_context_destroy(&self->transfer_contexts[i]); } z_poll_unref(self->poll); g_string_free(self->stack_info, TRUE); if (self->startup_lock) g_mutex_free(self->startup_lock); z_object_free_method(s); z_return(); } ZTransfer2Funcs z_transfer2_funcs = { { Z_FUNCS_COUNT(ZTransfer2), z_transfer2_free_method, }, NULL, NULL, NULL, NULL, NULL, NULL, .run = z_transfer2_run_method, .progress = NULL }; Z_CLASS_DEF(ZTransfer2, ZObject, z_transfer2_funcs); zorp-3.9.5/libproxy/zorp/000077500000000000000000000000001172670260400153715ustar00rootroot00000000000000zorp-3.9.5/libproxy/zorp/Makefile.am000066400000000000000000000000161172670260400174220ustar00rootroot00000000000000SUBDIRS=proxy zorp-3.9.5/libproxy/zorp/proxy/000077500000000000000000000000001172670260400165525ustar00rootroot00000000000000zorp-3.9.5/libproxy/zorp/proxy/Makefile.am000066400000000000000000000002071172670260400206050ustar00rootroot00000000000000ZORP_H = dottransfer.h transfer2.h errorloader.h polling_proxy.h pkgincludedir=@includedir@/zorp/proxy pkginclude_HEADERS = $(ZORP_H) zorp-3.9.5/libproxy/zorp/proxy/dottransfer.h000066400000000000000000000013371172670260400212620ustar00rootroot00000000000000#ifndef ZORP_DOTTRANSFER_H_INCLUDED #define ZORP_DOTTRANSFER_H_INCLUDED #include enum { DOT_DW_PREAMBLE = 0, DOT_DW_DATA = 1, DOT_DW_DATA_LF = 2, DOT_DW_DATA_DOT = 3, }; typedef struct _ZDotTransfer { ZTransfer2 super; gboolean previous_line_split; GString *preamble; guint preamble_ofs; guint dst_write_state; } ZDotTransfer; extern ZClass ZDotTransfer__class; ZDotTransfer * z_dot_transfer_new(ZClass *class, ZProxy *owner, ZPoll *poll, ZStream *client, ZStream *server, gsize buffer_size, glong timeout, gulong flags, GString *preamble); #endif zorp-3.9.5/libproxy/zorp/proxy/errorloader.h000066400000000000000000000010131172670260400212360ustar00rootroot00000000000000#ifndef ZORP_PROXY_ERRORLOADER_H_INCLUDED #define ZORP_PROXY_ERRORLOADER_H_INCLUDED #include enum { Z_EF_ESCAPE_NONE = 0x0001, Z_EF_ESCAPE_HTML = 0x0002, }; typedef gchar *(*ZErrorLoaderResolveFunc)(gchar *variable, gpointer user_data); typedef struct _ZErrorLoaderVarInfo { gchar *variable; ZErrorLoaderResolveFunc resolve; } ZErrorLoaderVarInfo; gchar * z_error_loader_format_file(gchar *filepath, gchar *additional_info, guint32 flags, ZErrorLoaderVarInfo *infos, gpointer user_data); #endif zorp-3.9.5/libproxy/zorp/proxy/polling_proxy.h000066400000000000000000000000001172670260400216160ustar00rootroot00000000000000zorp-3.9.5/libproxy/zorp/proxy/transfer2.h000066400000000000000000000160551172670260400206400ustar00rootroot00000000000000#ifndef ZORP_PROXY_TRANSFER2_H_INCLUDED #define ZORP_PROXY_TRANSFER2_H_INCLUDED #include #include #include #include #include typedef enum { ZT2_RESULT_FINISHED = 0, /* success */ ZT2_RESULT_SUSPENDED = 1, /* temporary suspend */ ZT2_RESULT_FAILED = 2, /* general error, continuation is possible */ ZT2_RESULT_ABORTED = 3, /* error, state is unknown, proxy should abort */ } ZTransfer2Result; #define ZT2F_COMPLETE_COPY 0x0001 #define ZT2F_PROXY_STREAMS_POLLED 0x0002 #define ZT2F_SUSPEND_NOOP 0x0004 /* transfer status constants */ #define ZT2S_FINISHED 0x0001 #define ZT2S_SUSPENDED 0x0002 #define ZT2S_FAILED 0x0004 #define ZT2S_TIMEDOUT 0x0008 #define ZT2S_ABORTED 0x0010 #define ZT2S_STARTED 0x0020 #define ZT2S_COPYING_TAIL 0x0040 #define ZT2S_EOF_SOURCE 0x0100 #define ZT2S_EOF_DEST 0x0200 #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 #define ZT2E_SOURCE 0 #define ZT2E_DEST 1 #define ZT2E_DOWN_SOURCE ZT2E_SOURCE | ZT2E_STACKED #define ZT2E_DOWN_DEST ZT2E_DEST | ZT2E_STACKED #define ZT2E_MAX 3 typedef struct _ZTransfer2Buffer ZTransfer2Buffer; typedef struct _ZTransfer2 ZTransfer2; struct _ZTransfer2Buffer { gchar *buf; gsize size; gsize ofs, end; }; struct _ZTransfer2 { ZObject super; ZProxy *owner; ZPoll *poll; ZTransfer2Buffer buffers[EP_MAX]; ZStream *endpoints[EP_MAX]; ZStreamContext transfer_contexts[EP_MAX]; ZStreamContext proxy_contexts[EP_MAX]; gsize buffer_size; glong timeout, progress_interval; guint32 flags; ZStackedProxy *stacked; GSource *timeout_source; GSource *progress_source; /* internal state */ guint32 status; gint suspend_reason; /* info returned by the stacked proxy */ const gchar *content_format; ZVerdict stack_decision; GString *stack_info; gint64 our_content_length_hint; gboolean our_content_length_hint_set; gint64 child_content_length_hint; gboolean child_content_length_hint_set; /* Note: This mutex save a race condition in * transfer startup. In the function z_transfer2_start * the stacking is called before z_transfer2_setup is called. * But after stacking an iface callback might be called and * it would be causing some problem. */ GMutex *startup_lock; }; typedef struct _ZTransfer2Funcs { ZObjectFuncs super; GIOStatus (*src_read)(ZTransfer2 *self, ZStream *s, gchar *buf, gsize count, gsize *bytes_read, GError **error); GIOStatus (*dst_write)(ZTransfer2 *self, ZStream *s, const gchar *buf, gsize count, gsize *bytes_written, GError **error); GIOStatus (*src_shutdown)(ZTransfer2 *self, ZStream *s, GError **error); GIOStatus (*dst_shutdown)(ZTransfer2 *self, ZStream *s, GError **error); gboolean (*stack_proxy)(ZTransfer2 *self, ZStackedProxy **stacked); gboolean (*setup)(ZTransfer2 *self); ZTransfer2Result (*run)(ZTransfer2 *self); gboolean (*progress)(ZTransfer2 *self); } ZTransfer2Funcs; extern ZClass ZTransfer2__class; gboolean z_transfer2_start(ZTransfer2 *self); void z_transfer2_suspend(ZTransfer2 *self, gint suspend_reason); gboolean z_transfer2_rollback(ZTransfer2 *self); gboolean z_transfer2_cancel(ZTransfer2 *self); void z_transfer2_enable_progress(ZTransfer2 *elf, glong progress_interval); gboolean z_transfer2_simple_run(ZTransfer2 *self); ZTransfer2 * z_transfer2_new(ZClass *class, ZProxy *owner, ZPoll *poll, ZStream *source, ZStream *dest, gsize buffer_size, glong timeout, guint32 flags); void z_transfer2_free_method(ZObject *s); static inline gint z_transfer2_get_suspend_reason(ZTransfer2 *self) { return self->suspend_reason; } static inline guint32 z_transfer2_get_status(ZTransfer2 *self, guint32 status_bit) { return !!(self->status & status_bit); } static inline void z_transfer2_set_stacked_proxy(ZTransfer2 *self, ZStackedProxy *stacked) { g_assert(!z_transfer2_get_status(self, ZT2S_STARTED)); if (self->stacked) z_stacked_proxy_destroy(self->stacked); self->stacked = stacked; } static inline void z_transfer2_set_content_format(ZTransfer2 *self, const gchar *content_format) { 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) { return self->stack_info->str; } static inline ZVerdict z_transfer2_get_stack_decision(ZTransfer2 *self) { return self->stack_decision; } /** * z_transfer2_get_stream: * @self: ZTransfer2 instance * @endpoint: endpoint index * * This function returns the stream associated to the endpoint specified * by @endpoint. @endpoint should be one of the ZT2E_* values. **/ static inline ZStream * z_transfer2_get_stream(ZTransfer2 *self, gint endpoint) { if (endpoint & ZT2E_STACKED) return self->stacked ? self->stacked->downstreams[endpoint & ~ZT2E_STACKED] : NULL; else return self->endpoints[endpoint]; } /* helper functions for virtual methods */ static inline GIOStatus z_transfer2_src_read(ZTransfer2 *self, ZStream *s, gchar *buf, gsize count, gsize *bytes_read, GError **err) { return Z_FUNCS(self, ZTransfer2)->src_read(self, s, buf, count, bytes_read, err); } static inline GIOStatus z_transfer2_dst_write(ZTransfer2 *self, ZStream *s, gchar *buf, gsize count, gsize *bytes_written, GError **err) { return Z_FUNCS(self, ZTransfer2)->dst_write(self, s, buf, count, bytes_written, err); } static inline GIOStatus z_transfer2_src_shutdown(ZTransfer2 *self, ZStream *s, GError **err) { if (Z_FUNCS(self, ZTransfer2)->src_shutdown) return Z_FUNCS(self, ZTransfer2)->src_shutdown(self, s, err); else return G_IO_STATUS_NORMAL; } static inline GIOStatus z_transfer2_dst_shutdown(ZTransfer2 *self, ZStream *s, GError **err) { if (Z_FUNCS(self, ZTransfer2)->dst_shutdown) return Z_FUNCS(self, ZTransfer2)->dst_shutdown(self, s, err); else return G_IO_STATUS_NORMAL; } static inline gboolean z_transfer2_stack_proxy(ZTransfer2 *self, ZStackedProxy **stacked) { if (Z_FUNCS(self, ZTransfer2)->stack_proxy) return Z_FUNCS(self, ZTransfer2)->stack_proxy(self, stacked); else return TRUE; } static inline ZTransfer2Result z_transfer2_run(ZTransfer2 *self) { return Z_FUNCS(self, ZTransfer2)->run(self); } static inline gboolean z_transfer2_setup(ZTransfer2 *self) { if (Z_FUNCS(self, ZTransfer2)->setup) return Z_FUNCS(self, ZTransfer2)->setup(self); else return TRUE; } static inline gboolean z_transfer2_progress(ZTransfer2 *self) { if (Z_FUNCS(self, ZTransfer2)->progress) return Z_FUNCS(self, ZTransfer2)->progress(self); else return TRUE; } #endif zorp-3.9.5/libzorp.pc.in000066400000000000000000000004021172670260400151350ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ datadir=@datadir@ datarootdir=@datarootdir@ Name: ZorpLib Description: Zorp Core Library Requires: zorplibll Version: @VERSION@ Libs: -lzorp -L@libdir@ Cflags: -I@includedir@ zorp-3.9.5/libzorpproxy.pc.in000066400000000000000000000004241172670260400162430ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ datadir=@datadir@ datarootdir=@datarootdir@ Name: ZorpLibProxy Description: Zorp Lib for Proxies Requires: libzorp Version: @VERSION@ Libs: -lzorp -lzorpproxy -L@libdir@ Cflags: -I@includedir@ zorp-3.9.5/module.list000066400000000000000000000000621172670260400147070ustar00rootroot00000000000000anypy finger ftp http plug pop3 smtp telnet whois zorp-3.9.5/moduledist.conf.in000066400000000000000000000002011172670260400161450ustar00rootroot00000000000000#Dependency lib informations PYTHON_CFLAGS=@PYTHON_CFLAGS@ PYTHON_LIBS=@PYTHON_LIBS@ OPENSSL_LIBS=@OPENSSL_LIBS@ CFLAGS=@CFLAGS@ zorp-3.9.5/modules/000077500000000000000000000000001172670260400141775ustar00rootroot00000000000000zorp-3.9.5/modules/Makefile.am000066400000000000000000000000741172670260400162340ustar00rootroot00000000000000SUBDIRS = anypy finger ftp http plug pop3 smtp telnet whois zorp-3.9.5/modules/anypy/000077500000000000000000000000001172670260400153375ustar00rootroot00000000000000zorp-3.9.5/modules/anypy/AnyPy.py000066400000000000000000000114661172670260400167610ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : Bazsi ## Auditor : kisza ## Last audited version: 1.2 ## Notes: ## ############################################################################ """ Module defining interface to the AnyPy proxy. This module defines an interface to the AnyPy proxy as implemented in Zorp. AnyPy is basically a Python proxy which means that the proxy behaviour is defined in Python by the administrator.
Related standards
""" from Proxy import Proxy # policy verdicts ANYPY_UNSPEC = 0 # policy doesn't specify it, do something sensible ANYPY_ACCEPT = 1 ANYPY_DENY = 2 ANYPY_REJECT = 3 # continue and tell the client that we didn't do it ANYPY_ABORT = 4 # abort the connection ANYPY_DROP = 5 # continue and don't do it ANYPY_POLICY = 6 # Policy level will decide what to do ANYPY_ERROR = 7 # Error occured try to nice fail class AbstractAnyPyProxy(Proxy): """ Class encapsulating in AnyPy proxy. This class encapsulates AnyPy, a proxy module calling a Python function to do all of its work. It can be used for defining proxies for protocols not directly supported by Zorp.
Note Your code will be running as the proxy to transmit protocol elements, you'll have to take care and be security conscious not to make security vulnerabilities.
""" name = "anypy" def __init__(self, session): """ Constructor to initialize an AnyPy instance. This constructor initializes a new AnyPy instance based on arguments and calls the inherited constructor. session SESSION session we belong to """ Proxy.__init__(self, session) def proxyThread(self): """ Function called by the low level proxy core to perform transferring requests. This function is called by the proxy module to perform transferring requests. It may use the 'self.session.client_stream' and 'self.session.server_stream' streams to read data from and write data to. """ raise NotImplementedError class AnyPyProxy(AbstractAnyPyProxy): """ Class encapsulating the default AnyPy proxy. """ pass zorp-3.9.5/modules/anypy/Makefile.am000066400000000000000000000003551172670260400173760ustar00rootroot00000000000000pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = AnyPy.py pkglib_LTLIBRARIES = libanypy.la libanypy_la_SOURCES = anypy.c EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/anypy/anypy.c000066400000000000000000000160331172670260400166460ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Bazsi * Auditor: Bazsi * Last audited version: 1.14 * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #define ANYPY_ERROR "anypy.error" typedef struct _AnyPyProxy { ZProxy super; guint max_line_length[EP_MAX]; } AnyPyProxy; extern ZClass AnyPyProxy__class; /** * anypy_stream_init: * @self: AnyPyProxy instance * * This function is called upon startup to initialize our streams. **/ static gboolean anypy_stream_init(AnyPyProxy *self) { z_proxy_enter(self); if (!self->super.endpoints[EP_CLIENT] || !self->super.endpoints[EP_SERVER]) { z_proxy_log(self, ANYPY_ERROR, 2, "Server side not yet connected, unable to init streams;"); z_proxy_leave(self); return FALSE; } self->super.endpoints[EP_CLIENT] = z_stream_push(self->super.endpoints[EP_CLIENT], z_stream_line_new(NULL, self->max_line_length[EP_CLIENT], ZRL_EOL_CRLF)); self->super.endpoints[EP_SERVER] = z_stream_push(self->super.endpoints[EP_SERVER], z_stream_line_new(NULL, self->max_line_length[EP_SERVER], ZRL_EOL_CRLF)); z_proxy_leave(self); return TRUE; } /** * anypy_set_verdict: * @self: AnyPyProxy instance * @args: Python args argument * * sets verdict for the parent proxy * args is (verdict,description) **/ static ZPolicyObj * anypy_set_verdict(AnyPyProxy * self, ZPolicyObj *args) { gint verdict; gchar *description; z_proxy_enter(self); if (!z_policy_var_parse_tuple(args, "is", &verdict, &description)) { z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid arguments."); z_proxy_return(self, NULL); } if (self->super.parent_proxy) { ZProxyStackIface *iface; iface = z_proxy_find_iface(self->super.parent_proxy, Z_CLASS(ZProxyStackIface)); if (iface) { z_proxy_stack_iface_set_verdict(iface, verdict, description); z_object_unref(&iface->super); } } z_proxy_return(self, z_policy_none_ref()); } /** * anypy_set_content_hint: * @self: AnyPyProxy instance * @args: Python args argument * * sets verdict for the parent proxy * args is (verdict,description) **/ static ZPolicyObj * anypy_set_content_hint(AnyPyProxy * self, ZPolicyObj *args) { gint64 length; z_proxy_enter(self); if (!z_policy_var_parse_tuple(args, "L", &length)) { z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid arguments."); z_proxy_leave(self); return NULL; } if (self->super.parent_proxy) { ZProxyStackIface *iface; iface = z_proxy_find_iface(self->super.parent_proxy, Z_CLASS(ZProxyStackIface)); if (iface) { z_proxy_stack_iface_set_content_hint(iface, length); z_object_unref(&iface->super); } } z_proxy_return(self, z_policy_none_ref()); } /** * anypy_config_set_defaults: * @self: AnyPyProxy instance * * This function initializes various attributes exported to the Python layer * for possible modification. **/ static void anypy_config_set_defaults(AnyPyProxy *self) { z_proxy_enter(self); self->max_line_length[EP_CLIENT] = 4096; self->max_line_length[EP_SERVER] = 4096; z_proxy_leave(self); } /** * anypy_register_vars: * @self: AyPyProxy instance * * This function is called upon startup to export Python attributes. **/ static void anypy_register_vars(AnyPyProxy *self) { z_proxy_enter(self); /* method for setting the proxy verdict. It should be used before the first write */ z_proxy_var_new(&self->super, "set_verdict", Z_VAR_TYPE_METHOD | Z_VAR_GET, self,anypy_set_verdict); /* method for setting the content hint. It should be used before the first write */ z_proxy_var_new(&self->super, "set_content_hint", Z_VAR_TYPE_METHOD | Z_VAR_GET, self, anypy_set_content_hint); /* size of line buffer of the client stream */ z_proxy_var_new(&self->super, "client_max_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_line_length[EP_CLIENT]); /* size of line buffer of the server stream */ z_proxy_var_new(&self->super, "server_max_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_line_length[EP_SERVER]); z_proxy_leave(self); } /** * anypy_config: * @s: AnyPyProxy instance casted to ZProxy * * This function is called upon startup to configure the proxy. * This calls the the __pre_config__, config and __post_config__ events. **/ static gboolean anypy_config(ZProxy *s) { AnyPyProxy *self = Z_CAST(s, AnyPyProxy); anypy_config_set_defaults(self); anypy_register_vars(self); if (Z_SUPER(s, ZProxy)->config(s)) { return TRUE; } return FALSE; } static void anypy_main(ZProxy * s) { AnyPyProxy *self = Z_CAST(s, AnyPyProxy); ZPolicyObj *res; gboolean called; z_proxy_enter(self); if (!z_proxy_connect_server(&self->super, NULL, 0) || !anypy_stream_init(self)) { z_proxy_leave(self); return; } z_policy_lock(self->super.thread); res = z_policy_call(self->super.handler, "proxyThread", NULL, &called, self->super.session_id); z_policy_var_unref(res); z_policy_unlock(self->super.thread); z_proxy_return(self); } /** * anypy_proxy_new: * @params: parameters for the AnyPyProxy class constructor * * This function is called upon startup to create a new AnyPy proxy. **/ ZProxy * anypy_proxy_new(ZProxyParams *params) { AnyPyProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(AnyPyProxy), params), AnyPyProxy); z_return(&self->super); } ZProxyFuncs anypy_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), NULL }, .config = anypy_config, .main = anypy_main, NULL }; Z_CLASS_DEF(AnyPyProxy, ZProxy, anypy_proxy_funcs); gint zorp_module_init(void) { z_registry_add("anypy", ZR_PYPROXY, anypy_proxy_new); return TRUE; } zorp-3.9.5/modules/finger/000077500000000000000000000000001172670260400154515ustar00rootroot00000000000000zorp-3.9.5/modules/finger/Finger.py000066400000000000000000000317301172670260400172410ustar00rootroot00000000000000############################################################################ ## ## COPYRIGHTHERE ## ## Author : Bazsi ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Proxy for the Finger User Information Protocol. The Finger module defines the classes constituting the proxy for the Finger protocol.
The Finger protocol Finger is a request/response based User Information Protocol using port TCP/79. The client opens a connection to the remote machine to initiate a request. The client sends a one line query based on the Finger query specification and waits for the answer. A remote user information program (RUIP) processes the query, returns the result and closes the connection. The response is a series of lines consisting of printable ASCII closed carriage return-line feed (CRLF, ASCII13, ASCII10). After receiving the answer the client closes the connection as well. The following queries can be used: <CRLF> This is a simple query listing all users logged in to the remote machine. USERNAME<CRLF> A query to request all available information about the user USERNAME. USERNAME@HOST1<CRLF> Request the RUIP to forward the query to HOST1. The response to this query is all information about the user USERNAME available at the remote computer HOST1. USERNAME@HOST1@HOST2<CRLF> Request HOST1 to forward the query to HOST2. The response to this query is all information about the user USERNAME available at the remote computer HOST2.
Proxy behavior Finger is a module built for parsing messages of the Finger protocol. It reads the QUERY at the client side, parses it and - if the local security policy permits - sends it to the server. When the RESPONSE arrives it processes the RESPONSE and sends it back to the client. It is possible to prepend and/or append a string to the response. Requests can also be manipulated in various ways using the fingerRequest function, which is called by the proxy if it is defined. Length of the username, the line and the hostname can be limited by setting various attributes. Finger proxy also has the capability of limiting the number of hosts in a request, e.g.: finger user@domain@server normally results in fingering 'user@domain' performed by the host 'server'. By default, the proxy removes everything after and including the first '@' character. This behavior can be modified by setting the max_hop_count attribute to a non-zero value. Controlling the number of max hops def MyFingerProxy(FingerProxy): def config(self): FingerProxy.config(self) self.max_hop_count = 2 self.timeout = 30
Related standards The Finger User Information Protocol is described in RFC 1288.
""" from Zorp import * from Proxy import Proxy class AbstractFingerProxy(Proxy): """ Class encapsulating the abstract Finger proxy. This proxy implements the Finger protocol as specified in RFC 1288. max_hop_count 0 Maximum number of '@' characters in the request. Any text after the last allowed '@' character is stripped from the request. max_hostname_length 30 Maximum number of characters in a single name of the hostname chain. max_line_length 132 Maximum number of characters in a single line in requests and responses. max_username_length 8 Maximum length of the username in a request. request_detailed n/a Indicates if multi-line formatting request (/W prefix) was sent by the client (-l parameter). Request for multi-line formatting can be added/removed by the proxy during the fingerRequest event. request_hostnames n/a The hostname chain. The hostname chain can be modified by the proxy during the fingerRequest event. request_username n/a The username to be queried. The username can be modified by the proxy during the fingerRequest event. response_header "" String to be prepended by the proxy to each finger response. response_footer String to be appended by the proxy to each finger response. strict_username_check TRUE If enabled (TRUE), only requests for usernames containing alphanumeric characters and underscore [a-zA-Z0-9_] are allowed. timeout n/a Timeout value for the request in milliseconds. """ name = "finger" def __init__(self, session): """ Constructor to initialize a FingerProxy instance. This constructor creates and set up a FingerProxy instance. session SESSION session this instance belongs to """ Proxy.__init__(self, session) def fingerRequest(self, username, hostname): """ Function processing finger requests. This function is called by the Finger proxy to process requests. It can also modify request-specific attributes. username Username to be fingered. hostname Destination hosts of the finger request. """ return ZV_ACCEPT def config(self): """ """ pass class FingerProxy(AbstractFingerProxy): """ Class encapsulating the default Finger proxy. Simple FingerProxy based on AbstractFingerProxy. """ pass zorp-3.9.5/modules/finger/Makefile.am000066400000000000000000000003611172670260400175050ustar00rootroot00000000000000pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Finger.py pkglib_LTLIBRARIES = libfinger.la libfinger_la_SOURCES = finger.c EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/finger/finger.c000066400000000000000000000460551172670260400171010ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Bazsi * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include /* log classes used by this module */ #define FINGER_DEBUG "finger.debug" #define FINGER_ERROR "finger.error" #define FINGER_POLICY "finger.policy" #define FINGER_REQUEST "finger.request" #define FINGER_VIOLATION "finger.violation" #define FINGER_REQ_UNSPEC ZV_UNSPEC #define FINGER_REQ_ACCEPT ZV_ACCEPT #define FINGER_REQ_DROP ZV_DROP #define FINGER_REQ_REJECT ZV_REJECT #define FINGER_REQ_ABORT ZV_ABORT /* * Finger proxy class. */ typedef struct _FingerProxy { ZProxy super; gint timeout; gboolean long_req; gint max_hop_count; guint max_line_length; guint max_username_length; guint max_hostname_length; gboolean strict_username_check; GString *username; GString *hostnames; GString *response_header; GString *response_footer; } FingerProxy; extern ZClass FingerProxy__class; /** * finger_config_set_defaults: * @self: FingerProxy instance * * Fills in our state with default values. **/ static void finger_config_set_defaults(FingerProxy *self) { z_proxy_enter(self); self->max_line_length = 132; self->max_username_length = 8; self->max_hostname_length = 30; self->max_hop_count = 0; self->strict_username_check = TRUE; self->username = g_string_sized_new(32); self->hostnames = g_string_sized_new(0); self->response_header = g_string_sized_new(0); self->response_footer = g_string_sized_new(0); self->timeout = 30000; z_proxy_return(self); } /** * finger_register_vars: * @self: FingerProxy instance * * Registers variables exported to the policy layer. **/ static void finger_register_vars(FingerProxy *self) { z_proxy_enter(self); #if 0 z_polict_dict_register(self->super.dict, Z_VT_INT, "timeout", Z_VF_READ | Z_VF_CFG_RW, &self->timeout, NULL, Z_VT_INT, "max_line_length", Z_VF_READ | Z_VF_CFG_RW, &self->max_line_length, NULL, Z_VT_INT, "max_username_length", Z_VF_READ | Z_VF_CFG_RW, &self->max_username_length, NULL, Z_VT_INT, "max_hostname_length", Z_VF_READ | Z_VF_CFG_RW, &self->max_hostname_length, NULL, Z_VT_INT, "max_hop_count", Z_VF_READ | Z_VF_CFG_RW, &self->max_hop_count, NULL, #endif z_proxy_var_new(&self->super, "timeout", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->timeout); z_proxy_var_new(&self->super, "max_line_length", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->max_line_length); z_proxy_var_new(&self->super, "max_username_length", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->max_username_length); z_proxy_var_new(&self->super, "max_hostname_length", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->max_hostname_length); z_proxy_var_new(&self->super, "max_hop_count", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->max_hop_count); z_proxy_var_new(&self->super, "request_detailed", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_INT, &self->long_req); z_proxy_var_new(&self->super, "long_request", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_ALIAS, "request_detailed"); z_proxy_var_new(&self->super, "request_username", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_STRING, self->username); z_proxy_var_new(&self->super, "username", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_ALIAS, "request_username"); z_proxy_var_new(&self->super, "request_hostnames", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_STRING, self->hostnames); z_proxy_var_new(&self->super, "hostnames", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_ALIAS, "request_hostnames"); z_proxy_var_new(&self->super, "response_header", Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_STRING, self->response_header); z_proxy_var_new(&self->super, "response_footer", Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_STRING, self->response_footer); z_proxy_var_new(&self->super, "strict_username_check", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->strict_username_check); z_proxy_return(self); } /** * finger_init_client_stream: * @self: FingerProxy instance * * Initialize our client stream. We allocate a readline instance so * that we can fetch input line by line. **/ static gboolean finger_init_client_stream(FingerProxy *self) { ZStream *tmpstream; z_proxy_enter(self); self->super.endpoints[EP_CLIENT]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_CLIENT]; self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_proxy_return(self, TRUE); } /** * finger_init_server_stream: * @self: FingerProxy instance * * Initialize our server stream. Exit with an error if our server side * is not connected. (ie NULL) **/ static gboolean finger_init_server_stream(FingerProxy *self) { ZStream *tmpstream; z_proxy_enter(self); if (!self->super.endpoints[EP_SERVER]) z_proxy_return(self, FALSE); self->super.endpoints[EP_SERVER]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_SERVER]; self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_proxy_return(self, TRUE); } /** * finger_fetch_request: * @self: FingerProxy instance * * Read and process a request. **/ static gboolean finger_fetch_request(FingerProxy *self) { gchar *p, *line; gint left, hop_count; gsize line_length; gint res; gboolean fetch_user = TRUE; guint hostlen = 0; z_proxy_enter(self); res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &line, &line_length, NULL); if (res != G_IO_STATUS_NORMAL) { /*LOG This message is appears when zorp cannot read finger request */ z_proxy_log(self, FINGER_ERROR, 1, "Error reading request;"); z_proxy_return(self, FALSE); } /*LOG This message is say about read finger request */ z_proxy_log(self, FINGER_REQUEST, 6, "Request details; req='%.*s'", (gint) line_length, line); p = line; left = line_length; self->long_req = FALSE; while (*p == ' ' && left) { p++; left--; } if (*p == '/') { p++; left--; if (*p == 'W') { self->long_req = TRUE; p++; left--; } else { /*LOG This message appear when zorp cannot parse request */ z_proxy_log(self, FINGER_VIOLATION, 1, "Parse error, dropping request; req='%.*s'", (gint) line_length, line); z_proxy_return(self, FALSE); } } while (*p == ' ' && left) { p++; left--; } hop_count = 0; g_string_truncate(self->username, 0); g_string_truncate(self->hostnames, 0); while (*p && left) { if (*p == '@') { if (self->max_hop_count != -1) { hop_count++; if (hop_count > self->max_hop_count) break; } fetch_user = FALSE; hostlen = 0; } if (self->strict_username_check && !(isalnum(*p) || *p == '_' || *p == '@' || *p == '.' || *p == '-')) { /*LOG This message say that zorp found an invalid character in finger request. */ z_proxy_log(self, FINGER_VIOLATION, 1, "Invalid character, dropping request; line='%.*s'", (gint) line_length, line); z_proxy_return(self, FALSE); } if (fetch_user) { g_string_append_c(self->username, *p); if (self->username->len > self->max_username_length) { /*LOG This message is about too long username found in the request. */ z_proxy_log(self, FINGER_VIOLATION, 1, "Username too long, dropping request; line='%.*s'", (gint) line_length, line); z_proxy_return(self, FALSE); } } else { g_string_append_c(self->hostnames, *p); if (hostlen > self->max_hostname_length) { /*LOG This message is appear when a too long hostname found in the request horname chain. */ z_proxy_log(self, FINGER_VIOLATION, 1, "One hostname is too long in hostname chain, dropping request; req='%.*s'", (gint) line_length, line); z_proxy_return(self, FALSE); } hostlen++; } p++; left--; } z_proxy_return(self, TRUE); } /** * finger_send_request: * @self: FingerProxy instance * * Construct and send a request to the server based on the state * stored by finger_fetch_request(). **/ static gboolean finger_send_request(FingerProxy *self) { gchar request[self->username->len + self->hostnames->len + 6]; gsize bytes_written; z_proxy_enter(self); if (self->long_req) { if (self->username->len > 0) { if (self->hostnames->len > 0) g_snprintf(request, sizeof(request), "/W %s%s\r\n", self->username->str, self->hostnames->str); else g_snprintf(request, sizeof(request), "/W %s\r\n", self->username->str); } else { if (self->hostnames->len > 0) g_snprintf(request, sizeof(request), "/W %s\r\n", self->hostnames->str); else g_snprintf(request, sizeof(request), "/W\r\n"); } } else { if (self->username->len > 0) { if (self->hostnames->len > 0) g_snprintf(request, sizeof(request), "%s%s\r\n", self->username->str, self->hostnames->str); else g_snprintf(request, sizeof(request), "%s\r\n", self->username->str); } else { if (self->hostnames->len > 0) g_snprintf(request, sizeof(request), "%s\r\n", self->hostnames->str); else g_snprintf(request, sizeof(request), "\r\n"); } } if (z_stream_write(self->super.endpoints[EP_SERVER], request, strlen(request), &bytes_written, NULL) != G_IO_STATUS_NORMAL) { /*LOG This message appear when some error found in server side. */ z_proxy_log(self, FINGER_ERROR, 1, "Error write request;"); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } /** * finger_copy_response: * @self: FingerProxy instance * * Copy server's response to the client. * **/ static gboolean finger_copy_response(FingerProxy *self) { gsize bytes_written; gint res = G_IO_STATUS_ERROR; z_proxy_enter(self); if (self->response_header->len && z_stream_write(self->super.endpoints[EP_CLIENT], self->response_header->str, self->response_header->len, &bytes_written, NULL) != G_IO_STATUS_NORMAL) { /*LOG This message appear when some error found in client side when writting the header. */ z_proxy_log(self, FINGER_ERROR, 1, "Error write request;"); z_proxy_return(self, FALSE); } while (1) { gchar *line; gsize line_len; gchar *response; if (!z_proxy_loop_iteration(&self->super)) break; res = z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &line_len, NULL); if (res != G_IO_STATUS_NORMAL) /* EOF or read error */ break; response = alloca(line_len + 3); memcpy(response, line, line_len); strcpy(response + line_len, "\r\n"); if (z_stream_write(self->super.endpoints[EP_CLIENT], response, line_len + 2, &bytes_written, NULL) != G_IO_STATUS_NORMAL) { /*LOG This message appear when some error found in client side when writting the response. */ z_proxy_log(self, FINGER_ERROR, 1, "Error write request;"); z_proxy_return(self, FALSE); } } if (res != G_IO_STATUS_ERROR && self->response_footer->len && z_stream_write(self->super.endpoints[EP_CLIENT], self->response_footer->str, self->response_footer->len, &bytes_written, NULL) != G_IO_STATUS_NORMAL) { /*LOG This message appear when some error found in client side when writting the footer. */ z_proxy_log(self, FINGER_ERROR, 1, "Error write request;"); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } /** * finger_query_policy: * @self: FingerProxy instance * * Check the policy about the current request. **/ static gboolean finger_query_policy(FingerProxy *self) { char *errmsg = "Policy violation, request denied.\r\n"; gsize bytes_written; gint res; z_proxy_enter(self); z_policy_lock(self->super.thread); res = z_policy_event(self->super.handler, "fingerRequest", z_policy_var_build("(ss)", self->username->str, self->hostnames->str), self->super.session_id); switch (res) { case FINGER_REQ_UNSPEC: case FINGER_REQ_REJECT: case FINGER_REQ_ABORT: /*LOG This message is about administrator decision to reject the finger session. */ z_proxy_log(self, FINGER_POLICY, 2, "Policy violation, abort session;"); z_stream_write(self->super.endpoints[EP_CLIENT], errmsg, strlen(errmsg), &bytes_written, NULL); /* fallthrough */ case FINGER_REQ_DROP: if (res == ZV_DROP) { /*LOG This message is about administrator decision to drop finger session. */ z_proxy_log(self, FINGER_POLICY, 2, "Policy violation, drop session;"); } z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); case FINGER_REQ_ACCEPT: default: break; } z_policy_unlock(self->super.thread); z_proxy_return(self, TRUE); } /** * finger_config: **/ static gboolean finger_config(ZProxy *s) { FingerProxy *self = Z_CAST(s, FingerProxy); finger_config_set_defaults(self); finger_register_vars(self); return Z_SUPER(s, ZProxy)->config(s); } /** * finger_main: * @s: FingerProxy instance * * main proxy routine. **/ static void finger_main(ZProxy *s) { FingerProxy *self = Z_CAST(s, FingerProxy); z_proxy_enter(self); if (!finger_init_client_stream(self)) z_proxy_return(self); /*LOG This debug message is about proxy state when start to fetching request */ z_proxy_log(self, FINGER_DEBUG, 6, "fetching request;"); if (!finger_fetch_request(self)) { char *errmsg = "Finger protocol or disallowed protocol element, request denied.\r\n"; gsize bytes_written; z_stream_write(self->super.endpoints[EP_CLIENT], errmsg, strlen(errmsg), &bytes_written, NULL); z_proxy_return(self); } /*LOG This debug message is about proxy state when finger fetched request and asking policy about it */ z_proxy_log(self, FINGER_DEBUG, 6, "asking policy;"); if (!finger_query_policy(self)) z_proxy_return(self); /*LOG This debug message is about proxy state when finger start connect to server. */ z_proxy_log(self, FINGER_DEBUG, 6, "connecting server;"); /* this sets the server side endpoint if successful */ if (!z_proxy_connect_server(&self->super, NULL, 0)) z_proxy_return(self); if (!finger_init_server_stream(self)) z_proxy_return(self); /*LOG This debug message is about proxy state when finger start send the request to server. */ z_proxy_log(self, FINGER_DEBUG, 6, "sending request;"); if (!finger_send_request(self)) z_proxy_return(self); /*LOG This debug message is about proxy state when finger start to copy server answer to client. */ z_proxy_log(self, FINGER_DEBUG, 6, "copying response;"); if (!finger_copy_response(self)) z_proxy_return(self); /*LOG This debug message is about proxy state when finger stop it's work. */ z_proxy_log(self, FINGER_DEBUG, 6, "everything is done;"); z_proxy_return(self); } /** * finger_proxy_new: * @params: ZProxyParams structure * * Finger proxy constructor. Allocates and initializes a proxy instance, * starts proxy thread. **/ static ZProxy * finger_proxy_new(ZProxyParams *params) { FingerProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(FingerProxy), params), FingerProxy); z_return((ZProxy *) self); } ZProxyFuncs finger_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), NULL }, .config = finger_config, .main = finger_main, NULL }; Z_CLASS_DEF(FingerProxy, ZProxy, finger_proxy_funcs); /*+ Module initialization function. Registers a new proxy type. +*/ gint zorp_module_init(void) { z_registry_add("finger", ZR_PROXY, finger_proxy_new); return TRUE; } zorp-3.9.5/modules/ftp/000077500000000000000000000000001172670260400147705ustar00rootroot00000000000000zorp-3.9.5/modules/ftp/Ftp.py000066400000000000000000002333301172670260400160770ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : sasa ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Proxy for the File Transfer Protocol. The Ftp module defines the classes constituting the proxy for the File Transfer Protocol (FTP).
The FTP protocol File Transfer Protocol (FTP) is a protocol to transport files via a reliable TCP connection between a client and a server. FTP uses two reliable TCP connections to transfer files: a simple TCP connection (usually referred to as the Control Channel) to transfer control information and a secondary TCP connection (usually referred to as the Data Channel) to perform the data transfer. It uses a command/response based approach, i.e. the client issues a command and the server responds with a 3-digit status code and associated status information in text format. The Data Channel can be initiated either from the client or the server; the Control Channel is always started from the client. The client is required to authenticate itself before other commands can be issued. This is performed using the USER and PASS commands specifying username and password, respectively.
Protocol elements The basic protocol is as follows: the client issues a request (also called command in FTP terminology) and the server responds with the result. Both commands and responses are line based: commands are sent as complete lines starting with a keyword identifying the operation to be performed. A response spans one or more lines, each specifying the same 3-digit status code and possible explanation.
Data transfer Certain commands (for example RETR, STOR or LIST) also have a data attachment which is transferred to the peer. Data attachments are transferred in a separate TCP connection. This connection is established on-demand on a random, unprivileged port when a data transfer command is issued. Endpoint information of this data channel is exchanged via the PASV and PORT commands, or their newer equivalents (EPSV and EPRT). The data connection can either be initiated by the client (passive mode) or the server (active mode). In passive mode (PASV or EPSV command) the server opens a listening socket and sends back the endpoint information in the PASV response. In active mode (PORT or EPRT command) the client opens a listening socket and sends its endpoint information as the argument of the PORT command. The source port of the server is usually either 20, or the port number of the Command Channel minus one. FTP protocol sample 220 FTP server ready USER account 331 Password required. PASS password 230 User logged in. SYST 215 UNIX Type: L8 PASV 227 Entering passive mode (192,168,1,1,4,0) LIST 150 Opening ASCII mode data connection for file list 226-Transferring data in separate connection complete. 226 Quotas off QUIT 221 Goodbye
Proxy behavior FtpProxy is a module built for parsing commands of the Control Channel in the FTP protocol. It reads the REQUEST at the client side, parses it and - if the local security policy permits - sends it to the server. The proxy parses the arriving RESPONSES and sends them to the client if the policy permits that. FtpProxy uses a PlugProxy to transfer the data arriving in the Data Channel. The proxy is capable of manipulating commands and stacking further proxies (e.g.: MimeProxy) into the Data Channel. Both transparent and non-transparent modes are supported. The default low-level proxy implementation (AbstractFtpProxy) denies all requests by default. Different commands and/or responses can be enabled by using one of the several predefined proxy classes which are suitable for most tasks. Alternatively, use of the commands can be permitted individually using different attributes. This is detailed in the following two sections.
Configuring policies for FTP commands and responses Changing the default behavior of commands can be done by using the hash attribute request, indexed by the command name (e.g.: USER or PWD). There is a similar attribute for responses called response, indexed by the command name and the response code. The possible values of these hashes are shown in the tables below. See for details. When looking up entries of the response attribute hash, the lookup precedence described in is used. Customizing FTP to allow only anonymous sessions This example calls a function called pUser (defined in the example) whenever a USER command is received. All other commands are accepted. The parameter of the USER command (i.e. the username) is examined: if it is 'anonymous' or 'Anonymous', the connection is accepted, otherwise it is rejected. class AnonFtp(FtpProxy): def config(self): self.request["USER"] = (FTP_REQ_POLICY, self.pUser) self.request["*"] = (FTP_REQ_ACCEPT) def pUser(self,command): if self.request_parameter == "anonymous" or self.request_parameter == "Anonymous": return FTP_REQ_ACCEPT return FTP_REQ_REJECT All responses are rejected by default, even though this is not permitted by RFC 959. Therefore, the FtpProxy sends an "500 Error parsing answer" to the client. The responses can be either enabled one by one, or all of them at once by using ("*","*") in the policy.
Configuring policies for FTP features and FTPS support FTP servers send the list of supported features to the clients. For example, proftpd supports the following features: LANG en, MDTM, UTF8, AUTH TLS, PBSZ, PROT, REST STREAM, SIZE. Zorp can change the default behavior of Ftp features using the hash attribute features, indexed by the name of the feature (e.g.: UTF8 or AUTH TLS). The possible actions are shown in the table below. See for details. The built-in Ftp proxies of Zorp permit the use of every feature by default.
Enabling FTPS connections For FTPS connections to operate correctly, the FTP server and client applications must comply to the FTP Security Extensions (RFC 2228) and Securing FTP with TLS (RFC 4217) RFCs. For FTPS connections, the AUTH TLS, PBSZ, PROT features must be accepted. Also, STARTTLS support must be properly configured. See for details. If the proxy is configured to disable encryption between Zorp and the client, the proxy automatically removes the AUTH TLS, PBSZ, PROT features from the list sent by the server. If STARTTLS connections are accepted on the client side (self.ssl.client_security=SSL_ACCEPT_STARTTLS), but TLS-forwarding is disabled on the server side, the proxy automatically inserts the AUTH TLS, PBSZ, PROT features into the list sent by the server. These features are inserted even if encryption is explicitly disabled on the server side or the server does not support the FEAT command, making one-sided STARTTLS support feasible. When using inband routing with the FTPS protocol, Zorp compares the server's certificate to its hostname. The subject_alt_name parameter (or the Common Name parameter if the subject_alt_name parameter is empty) of the server's certificate must contain the hostname or the IP address (as resolved from the Zorp host) of the server (e.g., ftp.example.com). Alternatively, the Common Name or the subject_alt_name parameter can contain a generic hostname, e.g., *.example.com. Note that if the Common Name of the certificate contains a generic hostname, do not specify a specific hostname or an IP address in the subject_alt_name parameter. The Zorp Ftp proxy does not support the following FTPS-related commands: REIN, CCC, CDC. STARTTLS is supported in nontransparent scenarios as well. Configuring FTPS support This example is a standard FtpProxy with FTPS support enabled. class FtpsProxy(FtpProxy): def config(self): self.ssl.client_connection_security = SSL_ACCEPT_STARTTLS self.ssl.server_connection_security = SSL_FORWARD_STARTTLS
Stacking The available stacking modes for this proxy module are listed in the following table. For additional information on stacking, see .
Configuring inband authentication Starting with Zorp 3.3FR1, the Ftp proxy supports inband authentication as well to use the built-in authentication method of the FTP and FTPS protocols to authenticate the client. The authentication itself is performed by the ZAS backend configured for the service. If the client uses different usernames on ZAS and the remote server (e.g., he uses his own username to authenticate to ZAS, but anonymous on the target FTP server), the client must specify the usernames and passwords in the following format: Username: <ftp user>@<proxy user>@<remote site>[:<port>] Password: <ftp password>@<proxy password> Alternatively, all the above information can be specified as the username: <ftp user>@<proxy user>@<remote site>[:<port>]:<ftp password>@<proxy password> When using inband routing with the FTPS protocol, Zorp compares the server's certificate to its hostname. The subject_alt_name parameter (or the Common Name parameter if the subject_alt_name parameter is empty) of the server's certificate must contain the hostname or the IP address (as resolved from the Zorp host) of the server (e.g., ftp.example.com). Alternatively, the Common Name or the subject_alt_name parameter can contain a generic hostname, e.g., *.example.com. Note that if the Common Name of the certificate contains a generic hostname, do not specify a specific hostname or an IP address in the subject_alt_name parameter.
Related standards The File Transfer Protocol is described in RFC 959. FTP Security Extensions including the FTPS protocol and securing FTP with TLS are described in RFC 2228 and RFC 4217.
Data flow control hashes. FTP_DATA_KEEPLeave untouched FTP_DATA_PASSIVEForce passive mode FTP_DATA_ACTIVEForce active mode Ftp proxyrequest control hashes. FTP_REQ_ACCEPT FTP_REQ_REJECT FTP_REQ_ABORT FTP_REQ_POLICY Ftp proxy response control hashes. FTP_RSP_ACCEPT FTP_RSP_REJECT FTP_RSP_ABORT FTP_RSP_POLICY Ftp proxy data port controll hashes. FTP_ACTIVE_MINUSONECommand port minus one FTP_ACTIVE_TWENTYPort 20 FTP_ACTIVE_RANDOMRandom port Stacking policy. FTP_STK_DATA FTP_STK_NONE Ftp feature control hash settings. FTP_FEATURE_ACCEPT FTP_FEATURE_DROP FTP_FEATURE_INSERT Ftp logging types, printed in to log messages FTP_DEBUG"ftp.debug" FTP_ERROR"ftp.error" FTP_POLICY"ftp.policy" Action codes for commands in FTP Allow the request to pass. Reject the request with the error message specified in the second optional parameter. Terminate the connection. Action codes for responses in FTP Allow the response to pass. Modify the response to a general failure with error message specified in the optional second parameter. Terminate the connection. Stacking policy. Pass the data to the stacked proxy or program. No proxy stacked. Policy about enabling FTP features. Forward the availability of the feature from the server to the client. Remove the feature from the feature list sent by the server. Add the feature into the list of available features.
""" from Zorp import * from Plug import PlugProxy from Proxy import Proxy, proxyLog from SockAddr import SockAddrInet, SockAddrInetRange from Session import StackedSession from Stream import Stream FTP_DATA_KEEP = 0 FTP_DATA_PASSIVE = 1 FTP_DATA_ACTIVE = 2 FTP_REQ_ACCEPT = 1 FTP_REQ_REJECT = 3 FTP_REQ_ABORT = 4 FTP_REQ_POLICY = 6 FTP_RSP_ACCEPT = 1 FTP_RSP_REJECT = 3 FTP_RSP_ABORT = 4 FTP_RSP_POLICY = 6 FTP_DEBUG = "ftp.debug" FTP_ERROR = "ftp.error" FTP_POLICY = "ftp.policy" FTP_ACTIVE_MINUSONE = 0 FTP_ACTIVE_TWENTY = 1 FTP_ACTIVE_RANDOM = 2 FTP_STK_NONE = 1 FTP_STK_DATA = 2 FTP_STK_POLICY = 6 FTP_FEATURE_ACCEPT = 1 FTP_FEATURE_DROP = 2 FTP_FEATURE_INSERT = 3 class ParseInbandAuthError(Exception): """""" pass class AbstractFtpProxy(Proxy): """ Class encapsulating the abstract FTP proxy. This proxy implements the FTP protocol as specified in RFC 959. All traffic and commands are denied by default. Consequently, either customized Ftp proxy classes derived from the abstract class should be used, or one of the predefined classes (e.g.: FtpProxy, FtpProxyRO, etc.). request_stack Hash containing the stacking policy for the FTP commands. The hash is indexed by the FTP command (e.g. RETR, STOR). See also . data_port_min 40000 On the proxy side, ports equal to or above the value of data_port_min can be allocated as the data channel. data_port_max 41000 On the proxy side, ports equal to or below the value of data_port_max can be allocated as the data channel. data_mode FTP_DATA_KEEP The type of the FTP connection on the server side can be manipulated: leave it as the client requested (FTP_DATA_KEEP), or force passive (FTP_DATA_PASSIVE) or active (FTP_DATA_ACTIVE) connection. masq_address_client "" IP address of the firewall appearing on the client side. If its value is set, Zorp sends this IP regardless of its true IP (where it is binded). This attribute may be used when network address translation is performed before Zorp. masq_address_server "" IP address of the firewall appearing on the server side. If its value is set, Zorp sends this IP regardless of its true IP (where it is binded). This attribute may be used when network address translation is performed before Zorp. max_line_length 255 Maximum length of a line that the proxy is allowed to transfer. Requests/responses exceeding this limit are dropped. max_username_length 32 Maximum length of the username. max_password_length 64 Maximum length of the password. max_hostname_length 128 Maximum length of hostname. Used only in non-transparent mode. password The password to be sent to the server. permit_unknown_command FALSE Enable the transmission of unknown commands. permit_empty_command TRUE Enable transmission of lines without commands. request Normative policy hash for FTP requests indexed by command name (e.g.: "USER", "PWD" etc.). See also . response Normative policy hash for FTP responses indexed by command name and answer code (e.g.: "USER","331"; "PWD","200" etc.). See also . request_command n/a When a request is evaluated on the policy level, this variable contains the requested command. request_parameter n/a When a request is evaluated on the policy level, this variable contains the parameters of the requested command. response_status When a response is evaluated on the policy level, this variable contains the answer code. response_parameter When a response is evaluated on the policy level, this variable contains answer parameters. response_strip_msg FALSE Strip the response message and only send the response code. target_port_range "21" The port where the client can connect through a non-transparent FtpProxy. timeout 300000 General I/O timeout in milliseconds. When there is no specific timeout for a given operation, this value is used. buffer_size 4096 Buffer size for data transfer in bytes. transparent_mode TRUE Specifies if the proxy works in transparent (TRUE) or non-transparent (FALSE) mode. username The username authenticated to the server. valid_chars_username "a-zA-Z0-9._@" List of the characters accepted in usernames. active_connection_mode FTP_ACTIVE_MINUSONE In active mode the server connects the client. By default this must be from Command Channel port minus one (FTP_ACTIVE_MINUSONE). Alternatively, connection can also be performed either from port number 20 (FTP_ACTIVE_TWENTY) or from a random port (FTP_ACTIVE_RANDOM). strict_port_checking TRUE If enabled Zorp will strictly check the foreign port: in active mode the server must be connected on port 20, while in any other situation the foreign port must be above 1023. permit_client_bounce_attack FALSE If enabled the IP addresses of data channels will not need to match with the IP address of the control channel, permitting the use of FXP while increasing the security risks. permit_server_bounce_attack FALSE If enabled the IP addresses of data channels will not need to match with the IP address of the control channel, permitting the use of FXP while increasing the security risks. max_continuous_line 100 Maximum number of answer lines for a command. hostname The hostname of the FTP server to connect to, when inband routing is used. hostport The port of the FTP server to connect to, when inband routing is used. proxy_username The username to be used for proxy authentication given by the user, when inband authentication is used. proxy_password The password to be used for proxy authentication given by the user, when inband authentication is used. auth n/a features Hash containing the filtering policy for FTP features. """ name = "ftp" auth_inband_supported = TRUE def __init__(self, session): """ Constructor to initialize an FtpProxy instance This constructor initializes an FtpProxy instance by calling the inherited __init__ constructor with appropriate parameters, and setting up local attributes based on arguments. session SESSION this session object """ self.restrict_client_connect = TRUE self.restrict_server_connect = FALSE self.data_port_min = 40000 self.data_port_max = 41000 self.request_stack = {} self.strict_port_checking = TRUE self.permit_client_bounce_attack = FALSE self.permit_server_bounce_attack = FALSE Proxy.__init__(self, session) def __destroy__(self): """ """ Proxy.__destroy__(self) try: del self.session.ftp_data_stop except AttributeError: pass def bounceCheck(self, remote, side, connect): """ Bounce check method for ftp. This function is called by the proxy to decide whether an incoming connection is mounting a bounce attack on this FTP service. The current behavior is to only allow data connections from the peers and not from anyone else, but this can be controlled by the permit_bounce_attack attribute. """ if side == 0: ret = (remote.ip == self.session.client_address.ip) or self.permit_client_bounce_attack if ret and self.strict_port_checking: if remote.port < 1024: ## LOG ## # This message indicates that the remote port is bellow 1024 and due to the # violation Zorp is closing connection. ## proxyLog(self, FTP_POLICY, 3, "Client foreign port below 1024; port='%d'" % remote.port) ret = FALSE elif side == 1: ret = (remote.ip == self.session.server_address.ip) or self.permit_server_bounce_attack if ret and self.strict_port_checking: if connect: if remote.port < 1024: ## LOG ## # This message indicates that the remote port is bellow 1024 and due to the # violation Zorp is closing connection. ## proxyLog(self, FTP_POLICY, 3, "Server foreign port below 1024 in passive mode; port='%d'" % remote.port) ret = FALSE else: if remote.port != 20 and remote.port != self.session.server_address.port - 1: ## LOG ## # This message indicates that the server's remote port is not control_port-1 or 20 and due to the # violation Zorp is closing connection. ## proxyLog(self, FTP_POLICY, 3, "Server foreign port is not good in active mode; port='%d', control_port='%d'" % (remote.port, self.session.server_address.port)) ret = FALSE else: ## LOG ## # This message indicates an internal error, please contact the BalaBit QA team. ## proxyLog(self, FTP_POLICY, 3, "Unknown side when calling bounceCheck; side='%d'" % side) ret = FALSE return ret def loadAnswers(self): """ This function can be called by derived classes to initialize internal hashtables. This function fills in the self.answers hash so that commonly used request/answer combinations are accepted. """ self.response["*", "421"] = (FTP_RSP_ABORT, "421 Logoff") self.response["*", "500"] = (FTP_RSP_ACCEPT) self.response["Null", "120"] = (FTP_RSP_ACCEPT) self.response["Null", "220"] = (FTP_RSP_ACCEPT) self.response["ABOR", "225"] = (FTP_RSP_ACCEPT) self.response["ABOR", "226"] = (FTP_RSP_ACCEPT) self.response["ABOR", "501"] = (FTP_RSP_ACCEPT) self.response["ABOR", "502"] = (FTP_RSP_ACCEPT) self.response["ACCT", "202"] = (FTP_RSP_ACCEPT) self.response["ACCT", "230"] = (FTP_RSP_ACCEPT) self.response["ACCT", "501"] = (FTP_RSP_ACCEPT) self.response["ACCT", "503"] = (FTP_RSP_ACCEPT) self.response["ACCT", "530"] = (FTP_RSP_ACCEPT) self.response["ALLO", "200"] = (FTP_RSP_ACCEPT) self.response["ALLO", "202"] = (FTP_RSP_ACCEPT) self.response["ALLO", "501"] = (FTP_RSP_ACCEPT) self.response["ALLO", "504"] = (FTP_RSP_ACCEPT) self.response["ALLO", "530"] = (FTP_RSP_ACCEPT) self.response["APPE", "110"] = (FTP_RSP_ACCEPT) self.response["APPE", "125"] = (FTP_RSP_ACCEPT) self.response["APPE", "150"] = (FTP_RSP_ACCEPT) self.response["APPE", "226"] = (FTP_RSP_ACCEPT) self.response["APPE", "250"] = (FTP_RSP_ACCEPT) self.response["APPE", "425"] = (FTP_RSP_ACCEPT) self.response["APPE", "426"] = (FTP_RSP_ACCEPT) self.response["APPE", "450"] = (FTP_RSP_ACCEPT) self.response["APPE", "451"] = (FTP_RSP_ACCEPT) self.response["APPE", "452"] = (FTP_RSP_ACCEPT) self.response["APPE", "501"] = (FTP_RSP_ACCEPT) self.response["APPE", "502"] = (FTP_RSP_ACCEPT) self.response["APPE", "530"] = (FTP_RSP_ACCEPT) self.response["APPE", "551"] = (FTP_RSP_ACCEPT) self.response["APPE", "552"] = (FTP_RSP_ACCEPT) self.response["APPE", "532"] = (FTP_RSP_ACCEPT) self.response["APPE", "534"] = (FTP_RSP_ACCEPT) self.response["APPE", "535"] = (FTP_RSP_ACCEPT) self.response["APPE", "550"] = (FTP_RSP_ACCEPT) self.response["APPE", "553"] = (FTP_RSP_ACCEPT) self.response["AUTH", "234"] = (FTP_RSP_ACCEPT) self.response["AUTH", "334"] = (FTP_RSP_ACCEPT) self.response["AUTH", "431"] = (FTP_RSP_ACCEPT) self.response["AUTH", "501"] = (FTP_RSP_ACCEPT) self.response["AUTH", "502"] = (FTP_RSP_ACCEPT) self.response["AUTH", "504"] = (FTP_RSP_ACCEPT) self.response["AUTH", "534"] = (FTP_RSP_ACCEPT) self.response["CDUP", "200"] = (FTP_RSP_ACCEPT) self.response["CDUP", "250"] = (FTP_RSP_ACCEPT) self.response["CDUP", "501"] = (FTP_RSP_ACCEPT) self.response["CDUP", "502"] = (FTP_RSP_ACCEPT) self.response["CDUP", "530"] = (FTP_RSP_ACCEPT) self.response["CDUP", "550"] = (FTP_RSP_ACCEPT) self.response["CWD", "250"] = (FTP_RSP_ACCEPT) self.response["CWD", "501"] = (FTP_RSP_ACCEPT) self.response["CWD", "502"] = (FTP_RSP_ACCEPT) self.response["CWD", "530"] = (FTP_RSP_ACCEPT) self.response["CWD", "550"] = (FTP_RSP_ACCEPT) self.response["DELE", "250"] = (FTP_RSP_ACCEPT) self.response["DELE", "450"] = (FTP_RSP_ACCEPT) self.response["DELE", "550"] = (FTP_RSP_ACCEPT) self.response["DELE", "501"] = (FTP_RSP_ACCEPT) self.response["DELE", "502"] = (FTP_RSP_ACCEPT) self.response["DELE", "530"] = (FTP_RSP_ACCEPT) self.response["EPRT", "200"] = (FTP_RSP_ACCEPT) self.response["EPRT", "501"] = (FTP_RSP_ACCEPT) self.response["EPRT", "522"] = (FTP_RSP_ACCEPT) self.response["EPSV", "229"] = (FTP_RSP_ACCEPT) self.response["EPSV", "501"] = (FTP_RSP_ACCEPT) self.response["FEAT", "221"] = (FTP_RSP_ACCEPT) self.response["FEAT", "502"] = (FTP_RSP_ACCEPT) self.response["HELP", "211"] = (FTP_RSP_ACCEPT) self.response["HELP", "214"] = (FTP_RSP_ACCEPT) self.response["HELP", "501"] = (FTP_RSP_ACCEPT) self.response["HELP", "502"] = (FTP_RSP_ACCEPT) self.response["LIST", "125"] = (FTP_RSP_ACCEPT) self.response["LIST", "150"] = (FTP_RSP_ACCEPT) self.response["LIST", "226"] = (FTP_RSP_ACCEPT) self.response["LIST", "250"] = (FTP_RSP_ACCEPT) self.response["LIST", "425"] = (FTP_RSP_ACCEPT) self.response["LIST", "426"] = (FTP_RSP_ACCEPT) self.response["LIST", "451"] = (FTP_RSP_ACCEPT) self.response["LIST", "450"] = (FTP_RSP_ACCEPT) self.response["LIST", "501"] = (FTP_RSP_ACCEPT) self.response["LIST", "502"] = (FTP_RSP_ACCEPT) self.response["LIST", "530"] = (FTP_RSP_ACCEPT) self.response["LIST", "534"] = (FTP_RSP_ACCEPT) self.response["LIST", "535"] = (FTP_RSP_ACCEPT) self.response["MDTM", "213"] = (FTP_RSP_ACCEPT) self.response["MDTM", "501"] = (FTP_RSP_ACCEPT) #Hmmm. self.response["MDTM", "550"] = (FTP_RSP_ACCEPT) self.response["MKD", "257"] = (FTP_RSP_ACCEPT) self.response["MKD", "501"] = (FTP_RSP_ACCEPT) self.response["MKD", "502"] = (FTP_RSP_ACCEPT) self.response["MKD", "530"] = (FTP_RSP_ACCEPT) self.response["MKD", "550"] = (FTP_RSP_ACCEPT) self.response["MLST", "250"] = (FTP_RSP_ACCEPT) self.response["MLST", "425"] = (FTP_RSP_ACCEPT) self.response["MLST", "426"] = (FTP_RSP_ACCEPT) self.response["MLST", "451"] = (FTP_RSP_ACCEPT) self.response["MLST", "450"] = (FTP_RSP_ACCEPT) self.response["MLST", "501"] = (FTP_RSP_ACCEPT) self.response["MLST", "502"] = (FTP_RSP_ACCEPT) self.response["MLST", "530"] = (FTP_RSP_ACCEPT) self.response["MLST", "534"] = (FTP_RSP_ACCEPT) self.response["MLST", "535"] = (FTP_RSP_ACCEPT) self.response["MLST", "550"] = (FTP_RSP_ACCEPT) self.response["MLSD", "125"] = (FTP_RSP_ACCEPT) self.response["MLSD", "150"] = (FTP_RSP_ACCEPT) self.response["MLSD", "226"] = (FTP_RSP_ACCEPT) self.response["MLSD", "250"] = (FTP_RSP_ACCEPT) self.response["MLSD", "425"] = (FTP_RSP_ACCEPT) self.response["MLSD", "426"] = (FTP_RSP_ACCEPT) self.response["MLSD", "451"] = (FTP_RSP_ACCEPT) self.response["MLSD", "450"] = (FTP_RSP_ACCEPT) self.response["MLSD", "501"] = (FTP_RSP_ACCEPT) self.response["MLSD", "502"] = (FTP_RSP_ACCEPT) self.response["MLSD", "530"] = (FTP_RSP_ACCEPT) self.response["MLSD", "534"] = (FTP_RSP_ACCEPT) self.response["MLSD", "535"] = (FTP_RSP_ACCEPT) self.response["MODE", "200"] = (FTP_RSP_ACCEPT) self.response["MODE", "501"] = (FTP_RSP_ACCEPT) self.response["MODE", "504"] = (FTP_RSP_ACCEPT) self.response["MODE", "530"] = (FTP_RSP_ACCEPT) self.response["NLST", "125"] = (FTP_RSP_ACCEPT) self.response["NLST", "150"] = (FTP_RSP_ACCEPT) self.response["NLST", "226"] = (FTP_RSP_ACCEPT) self.response["NLST", "250"] = (FTP_RSP_ACCEPT) self.response["NLST", "425"] = (FTP_RSP_ACCEPT) self.response["NLST", "426"] = (FTP_RSP_ACCEPT) self.response["NLST", "450"] = (FTP_RSP_ACCEPT) self.response["NLST", "451"] = (FTP_RSP_ACCEPT) self.response["NLST", "501"] = (FTP_RSP_ACCEPT) self.response["NLST", "502"] = (FTP_RSP_ACCEPT) self.response["NLST", "530"] = (FTP_RSP_ACCEPT) self.response["NLST", "534"] = (FTP_RSP_ACCEPT) self.response["NLST", "535"] = (FTP_RSP_ACCEPT) self.response["NLST", "550"] = (FTP_RSP_ACCEPT) self.response["NOOP", "200"] = (FTP_RSP_ACCEPT) self.response["PASS", "202"] = (FTP_RSP_ACCEPT) self.response["PASS", "230"] = (FTP_RSP_ACCEPT) self.response["PASS", "332"] = (FTP_RSP_ACCEPT) self.response["PASS", "501"] = (FTP_RSP_ACCEPT) self.response["PASS", "503"] = (FTP_RSP_ACCEPT) self.response["PASS", "530"] = (FTP_RSP_ACCEPT) self.response["PASV", "227"] = (FTP_RSP_ACCEPT) self.response["PASV", "501"] = (FTP_RSP_ACCEPT) self.response["PASV", "502"] = (FTP_RSP_ACCEPT) self.response["PASV", "530"] = (FTP_RSP_ACCEPT) self.response["PBSZ", "200"] = (FTP_RSP_ACCEPT) self.response["PBSZ", "501"] = (FTP_RSP_ACCEPT) self.response["PBSZ", "503"] = (FTP_RSP_ACCEPT) self.response["PBSZ", "530"] = (FTP_RSP_ACCEPT) self.response["PORT", "200"] = (FTP_RSP_ACCEPT) self.response["PORT", "501"] = (FTP_RSP_ACCEPT) self.response["PORT", "530"] = (FTP_RSP_ACCEPT) self.response["PROT", "200"] = (FTP_RSP_ACCEPT) self.response["PROT", "431"] = (FTP_RSP_ACCEPT) self.response["PROT", "501"] = (FTP_RSP_ACCEPT) self.response["PROT", "503"] = (FTP_RSP_ACCEPT) self.response["PROT", "504"] = (FTP_RSP_ACCEPT) self.response["PROT", "530"] = (FTP_RSP_ACCEPT) self.response["PROT", "534"] = (FTP_RSP_ACCEPT) self.response["PROT", "536"] = (FTP_RSP_ACCEPT) self.response["PWD", "257"] = (FTP_RSP_ACCEPT) self.response["PWD", "501"] = (FTP_RSP_ACCEPT) self.response["PWD", "502"] = (FTP_RSP_ACCEPT) self.response["PWD", "550"] = (FTP_RSP_ACCEPT) self.response["QUIT", "221"] = (FTP_RSP_ACCEPT) self.response["REIN", "120"] = (FTP_RSP_ACCEPT) self.response["REIN", "220"] = (FTP_RSP_ACCEPT) self.response["REIN", "502"] = (FTP_RSP_ACCEPT) self.response["REST", "350"] = (FTP_RSP_ACCEPT) self.response["REST", "501"] = (FTP_RSP_ACCEPT) self.response["REST", "502"] = (FTP_RSP_ACCEPT) self.response["REST", "530"] = (FTP_RSP_ACCEPT) self.response["RETR", "110"] = (FTP_RSP_ACCEPT) self.response["RETR", "125"] = (FTP_RSP_ACCEPT) self.response["RETR", "150"] = (FTP_RSP_ACCEPT) self.response["RETR", "226"] = (FTP_RSP_ACCEPT) self.response["RETR", "250"] = (FTP_RSP_ACCEPT) self.response["RETR", "425"] = (FTP_RSP_ACCEPT) self.response["RETR", "426"] = (FTP_RSP_ACCEPT) self.response["RETR", "450"] = (FTP_RSP_ACCEPT) self.response["RETR", "451"] = (FTP_RSP_ACCEPT) self.response["RETR", "452"] = (FTP_RSP_ACCEPT) self.response["RETR", "501"] = (FTP_RSP_ACCEPT) self.response["RETR", "530"] = (FTP_RSP_ACCEPT) self.response["RETR", "532"] = (FTP_RSP_ACCEPT) self.response["RETR", "534"] = (FTP_RSP_ACCEPT) self.response["RETR", "535"] = (FTP_RSP_ACCEPT) self.response["RETR", "550"] = (FTP_RSP_ACCEPT) self.response["RETR", "553"] = (FTP_RSP_ACCEPT) self.response["RMD", "250"] = (FTP_RSP_ACCEPT) self.response["RMD", "501"] = (FTP_RSP_ACCEPT) self.response["RMD", "502"] = (FTP_RSP_ACCEPT) self.response["RMD", "530"] = (FTP_RSP_ACCEPT) self.response["RMD", "550"] = (FTP_RSP_ACCEPT) self.response["RNFR", "350"] = (FTP_RSP_ACCEPT) self.response["RNFR", "450"] = (FTP_RSP_ACCEPT) self.response["RNFR", "501"] = (FTP_RSP_ACCEPT) self.response["RNFR", "502"] = (FTP_RSP_ACCEPT) self.response["RNFR", "530"] = (FTP_RSP_ACCEPT) self.response["RNFR", "550"] = (FTP_RSP_ACCEPT) self.response["RNTO", "250"] = (FTP_RSP_ACCEPT) self.response["RNTO", "501"] = (FTP_RSP_ACCEPT) self.response["RNTO", "502"] = (FTP_RSP_ACCEPT) self.response["RNTO", "530"] = (FTP_RSP_ACCEPT) self.response["RNTO", "532"] = (FTP_RSP_ACCEPT) self.response["RNTO", "553"] = (FTP_RSP_ACCEPT) self.response["SITE", "200"] = (FTP_RSP_ACCEPT) self.response["SITE", "202"] = (FTP_RSP_ACCEPT) self.response["SITE", "501"] = (FTP_RSP_ACCEPT) self.response["SITE", "530"] = (FTP_RSP_ACCEPT) self.response["SIZE", "213"] = (FTP_RSP_ACCEPT) self.response["SIZE", "550"] = (FTP_RSP_ACCEPT) self.response["SMNT", "202"] = (FTP_RSP_ACCEPT) self.response["SMNT", "250"] = (FTP_RSP_ACCEPT) self.response["SMNT", "501"] = (FTP_RSP_ACCEPT) self.response["SMNT", "502"] = (FTP_RSP_ACCEPT) self.response["SMNT", "530"] = (FTP_RSP_ACCEPT) self.response["SMNT", "550"] = (FTP_RSP_ACCEPT) self.response["STAT", "211"] = (FTP_RSP_ACCEPT) self.response["STAT", "212"] = (FTP_RSP_ACCEPT) self.response["STAT", "213"] = (FTP_RSP_ACCEPT) self.response["STAT", "450"] = (FTP_RSP_ACCEPT) self.response["STAT", "501"] = (FTP_RSP_ACCEPT) self.response["STAT", "502"] = (FTP_RSP_ACCEPT) self.response["STAT", "530"] = (FTP_RSP_ACCEPT) self.response["STOR", "110"] = (FTP_RSP_ACCEPT) self.response["STOR", "125"] = (FTP_RSP_ACCEPT) self.response["STOR", "150"] = (FTP_RSP_ACCEPT) self.response["STOR", "226"] = (FTP_RSP_ACCEPT) self.response["STOR", "250"] = (FTP_RSP_ACCEPT) self.response["STOR", "425"] = (FTP_RSP_ACCEPT) self.response["STOR", "426"] = (FTP_RSP_ACCEPT) self.response["STOR", "450"] = (FTP_RSP_ACCEPT) self.response["STOR", "451"] = (FTP_RSP_ACCEPT) self.response["STOR", "452"] = (FTP_RSP_ACCEPT) self.response["STOR", "501"] = (FTP_RSP_ACCEPT) self.response["STOR", "530"] = (FTP_RSP_ACCEPT) self.response["STOR", "532"] = (FTP_RSP_ACCEPT) self.response["STOR", "534"] = (FTP_RSP_ACCEPT) self.response["STOR", "535"] = (FTP_RSP_ACCEPT) self.response["STOR", "550"] = (FTP_RSP_ACCEPT) self.response["STOR", "551"] = (FTP_RSP_ACCEPT) self.response["STOR", "552"] = (FTP_RSP_ACCEPT) self.response["STOR", "553"] = (FTP_RSP_ACCEPT) self.response["STOU", "110"] = (FTP_RSP_ACCEPT) self.response["STOU", "125"] = (FTP_RSP_ACCEPT) self.response["STOU", "150"] = (FTP_RSP_ACCEPT) self.response["STOU", "226"] = (FTP_RSP_ACCEPT) self.response["STOU", "250"] = (FTP_RSP_ACCEPT) self.response["STOU", "425"] = (FTP_RSP_ACCEPT) self.response["STOU", "426"] = (FTP_RSP_ACCEPT) self.response["STOU", "450"] = (FTP_RSP_ACCEPT) self.response["STOU", "451"] = (FTP_RSP_ACCEPT) self.response["STOU", "452"] = (FTP_RSP_ACCEPT) self.response["STOU", "501"] = (FTP_RSP_ACCEPT) self.response["STOU", "530"] = (FTP_RSP_ACCEPT) self.response["STOU", "532"] = (FTP_RSP_ACCEPT) self.response["STOU", "534"] = (FTP_RSP_ACCEPT) self.response["STOU", "535"] = (FTP_RSP_ACCEPT) self.response["STOU", "551"] = (FTP_RSP_ACCEPT) self.response["STOU", "552"] = (FTP_RSP_ACCEPT) self.response["STOU", "553"] = (FTP_RSP_ACCEPT) self.response["STRU", "200"] = (FTP_RSP_ACCEPT) self.response["STRU", "501"] = (FTP_RSP_ACCEPT) self.response["STRU", "504"] = (FTP_RSP_ACCEPT) self.response["STRU", "530"] = (FTP_RSP_ACCEPT) self.response["SYST", "215"] = (FTP_RSP_ACCEPT) self.response["SYST", "501"] = (FTP_RSP_ACCEPT) self.response["SYST", "502"] = (FTP_RSP_ACCEPT) self.response["TYPE", "200"] = (FTP_RSP_ACCEPT) self.response["TYPE", "501"] = (FTP_RSP_ACCEPT) self.response["TYPE", "504"] = (FTP_RSP_ACCEPT) self.response["TYPE", "530"] = (FTP_RSP_ACCEPT) self.response["USER", "230"] = (FTP_RSP_ACCEPT) self.response["USER", "232"] = (FTP_RSP_ACCEPT) self.response["USER", "331"] = (FTP_RSP_ACCEPT) self.response["USER", "332"] = (FTP_RSP_ACCEPT) self.response["USER", "336"] = (FTP_RSP_ACCEPT) self.response["USER", "501"] = (FTP_RSP_ACCEPT) self.response["USER", "530"] = (FTP_RSP_ACCEPT) def loadMinimalCommands(self): """ This function enable some minimal command set This function loads a minimal set of commands, for various subclass """ self.request["ABOR"] = (FTP_REQ_ACCEPT) self.request["ACCT"] = (FTP_REQ_ACCEPT) self.request["AUTH"] = (FTP_REQ_ACCEPT) self.request["CDUP"] = (FTP_REQ_ACCEPT) self.request["CWD"] = (FTP_REQ_ACCEPT) self.request["EPRT"] = (FTP_REQ_ACCEPT) self.request["EPSV"] = (FTP_REQ_ACCEPT) self.request["FEAT"] = (FTP_REQ_ACCEPT) self.request["LIST"] = (FTP_REQ_ACCEPT) self.request["MODE"] = (FTP_REQ_ACCEPT) self.request["MDTM"] = (FTP_REQ_ACCEPT) self.request["MLST"] = (FTP_REQ_ACCEPT) self.request["MLSD"] = (FTP_REQ_ACCEPT) self.request["NLST"] = (FTP_REQ_ACCEPT) self.request["NOOP"] = (FTP_REQ_ACCEPT) self.request["PASV"] = (FTP_REQ_ACCEPT) self.request["PASS"] = (FTP_REQ_ACCEPT) self.request["PBSZ"] = (FTP_REQ_ACCEPT) self.request["PORT"] = (FTP_REQ_ACCEPT) self.request["PROT"] = (FTP_REQ_ACCEPT) self.request["PWD"] = (FTP_REQ_ACCEPT) self.request["QUIT"] = (FTP_REQ_ACCEPT) self.request["REST"] = (FTP_REQ_ACCEPT) self.request["RETR"] = (FTP_REQ_ACCEPT) self.request["SIZE"] = (FTP_REQ_ACCEPT) self.request["STAT"] = (FTP_REQ_ACCEPT) self.request["STRU"] = (FTP_REQ_ACCEPT) self.request["SYST"] = (FTP_REQ_ACCEPT) self.request["TYPE"] = (FTP_REQ_ACCEPT) self.request["CLNT"] = (FTP_REQ_REJECT) self.request["XPWD"] = (FTP_REQ_REJECT) self.request["MACB"] = (FTP_REQ_REJECT) self.request["OPTS"] = (FTP_REQ_REJECT) def requestStack(self): """ """ try: stack_proxy = self.request_stack[self.request_command] except: try: stack_proxy = self.request_stack["*"] except: stack_proxy = (FTP_STK_NONE, None) if type(stack_proxy) == type(()): while 1: stack_type = stack_proxy[0] if stack_type == FTP_STK_NONE: return (FTP_STK_NONE, None) elif stack_type == FTP_STK_POLICY: # call function stack_proxy = stack_proxy[1]() else: return stack_proxy else: return (FTP_STK_NONE, None) return stack_proxy def parseInbandAuth(self, command, parameter): """ This method should be called when inband authentication is used, to parse the data embedded in USER and PASS commands. This method fills in self.username, self.proxy_username, self.proxy_password, self.hostname, self.hostport and self.password from the USER and PASS command parameters passed to it. It will leave any unspecified fields untouched. """ def parseUser(self, parameter): self.need_proxy_pass = True self.need_ftp_pass = True ats = parameter.count('@') if ats == 0: # client configured for transparent/proxy-less operation raise ParseInbandAuthError, "USER parameter is required to include at least the username and the hostname in nontransparent mode" elif ats == 1: # old-style: USER: user@host[:port] PASS: ftp_pass self.username, hostname_port = parameter.split('@') # no proxy authentication is done in this case self.need_proxy_pass = False elif ats == 2: # USER: user@proxyUser@host[:port] PASS: pass@proxyPass self.username, self.proxy_username, hostname_port = parameter.split('@') # in this case, the PASS parameter includes the two passwords, therefore both need_* fields are left True elif ats == 3: # USER: user@proxyUser@host[:port]:pass@proxyPass PASS doesn't matter self.username, self.proxy_username, hostname_port_pass, self.proxy_password = parameter.split('@') self.need_proxy_pass = False self.proxy_auth_needed = 1 # tell C code to do authentication self.hostname, rest = hostname_port_pass.split(':', 1) try: port_s, self.password = rest.split(':', 1) try: hostport = int(port_s) if hostport < 1 or hostport > 65535: self.password = rest else: self.hostport = hostport except ValueError: self.password = rest except ValueError: self.password = rest self.need_ftp_pass = False else: # none of the above forms allow @-s except as separators raise ParseInbandAuthError, "too many \"@\"-s in USER parameter" if ats in (1, 2): try: self.hostname, port_s = hostname_port.split(':') except ValueError: self.hostname = hostname_port else: try: self.hostport = int(port_s) except ValueError: raise ParseInbandAuthError, "non-numeric port in USER parameter" def parsePass(self, parameter): if self.need_proxy_pass: try: self.password, self.proxy_password = parameter.split('@', 1) self.proxy_auth_needed = 1 # tell C code to do authentication except ValueError: raise ParseInbandAuthError, "proxy and FTP server passwords must be given in either "\ "USER or PASS parameter in nontransparent mode" elif self.need_ftp_pass: self.password = parameter # repeated PASS-es until another USER have no effect self.need_proxy_pass = False self.need_ftp_pass = False command = command.upper() try: if command == 'USER': parseUser(self, parameter) elif command == 'PASS': parsePass(self, parameter) else: proxyLog(self, FTP_POLICY, 3, "Error parsing inband authorization token, " \ "unknown command; command='%s'" % command) return FALSE except ParseInbandAuthError, e: proxyLog(self, FTP_POLICY, 3, "Error parsing inband authorization token; " \ "command='%s', parameter='%s', error='%s'" % (command, parameter, e.args[0])) return FALSE except ValueError, e: proxyLog(self, FTP_POLICY, 3, "Error parsing inband authorization token, " \ "input does not match the supported format; " \ "command='%s', parameter='%s'" % (command, parameter)) return FALSE return TRUE class FtpProxy(AbstractFtpProxy): """ Default Ftp proxy based on AbstractFtpProxy. A permitting Ftp proxy based on the AbstractFtpProxy, allowing all commands, responses, and features, including unknown ones. The connection is terminated if a response with the answer code 421 is received. """ def config(self): """ Configuration for FtpProxy. Enables all commands by setting permit_unknown_commands to TRUE and adding two wildcard entries to the commands and answers hash. """ self.request["*"] = (FTP_REQ_ACCEPT) self.response["*","421"] = (FTP_RSP_ABORT, "421 Logoff") self.response["*", "*"] = (FTP_RSP_ACCEPT) self.features["*"] = (FTP_FEATURE_ACCEPT) self.permit_unknown_command = TRUE class FtpProxyAnonRO(AbstractFtpProxy): """ FTP proxy based on AbstractFtpProxy, only allowing read-only access to anonymous users. FTP proxy based on AbstractFtpProxy, enabling read-only access (i.e. only downloading) to anonymous users (uploads and usernames other than 'anonymous' or 'ftp' are disabled). Commands and return codes are strictly checked, unknown commands and responses are rejected. Every feature is accepted. The ABOR; ACCT; AUTH; CDUP; CWD; EPRT; EPSV; FEAT; LIST; MODE; MDTM; NLST; NOOP; PASV; PASS; PORT; PWD; QUIT; REST; RETR; SIZE; STAT; STRU; SYST; TYPE; and USER commands are permitted, the CLNT; XPWD; MACB; OPTS commands are rejected. """ def pUser(self,command): """ """ if self.request_parameter == "ftp" or self.request_parameter == "anonymous": return FTP_REQ_ACCEPT return FTP_REQ_REJECT def config(self): """ Configuration for FtpProxyAnonRO It enables a minimal set of commands for a working anonymous Download-Only FTP proxy, and sets permit_unknown_commands to FALSE. """ AbstractFtpProxy.loadMinimalCommands(self) self.request["USER"] = (FTP_REQ_POLICY, self.pUser) self.request["*"] = (FTP_REQ_REJECT) AbstractFtpProxy.loadAnswers(self) self.response["*","*"] = (FTP_RSP_REJECT) self.features["*"] = (FTP_FEATURE_ACCEPT) self.permit_unknown_command = FALSE class FtpProxyRO(AbstractFtpProxy): """ FTP proxy based on AbstractFtpProxy, allowing read-only access to any user. FTP proxy based on AbstractFtpProxy, enabling read-only access to any user. Commands and return codes are strictly checked, unknown commands and responses are rejected. Every feature is accepted. The ABOR; ACCT; AUTH; CDUP; CWD; EPRT; EPSV; FEAT; LIST; MODE; MDTM; NLST; NOOP; PASV; PASS; PORT; PWD; QUIT; REST; RETR; SIZE; STAT; STRU; SYST; TYPE; and USER commands are permitted, the CLNT; XPWD; MACB; OPTS commands are rejected. """ def config(self): """ Configuration for FtpProxyRO It enables a minimal set of commands for a working Download-Only FTP proxy, and sets permit_unknown_commands to FALSE. """ AbstractFtpProxy.loadMinimalCommands(self) self.request["USER"] = (FTP_REQ_ACCEPT) self.request["*"] = (FTP_REQ_REJECT) AbstractFtpProxy.loadAnswers(self) self.response["*","*"] = (FTP_RSP_REJECT) self.features["*"] = (FTP_FEATURE_ACCEPT) self.permit_unknown_command = FALSE class FtpProxyAnonRW(AbstractFtpProxy): """ FTP proxy based on AbstractFtpProxy, allowing full read-write access, but only to anonymous users. FTP proxy based on AbstractFtpProxy, enabling full read-write access to anonymous users (the 'anonymous' and 'ftp' usernames are permitted). Commands and return codes are strictly checked, unknown commands and responses are rejected. Every feature is accepted. The ABOR; ACCT; APPE; CDUP; CWD; DELE; EPRT; EPSV; LIST; MKD; MODE; MDTM; NLST; NOOP; PASV; PASS; PORT; PWD; QUIT; RMD; RNFR; RNTO; REST; RETR; SIZE; STAT; STOR; STOU; STRU; SYST; TYPE; USER and FEAT commands are permitted, the AUTH; CLNT; XPWD; MACB; OPTS commands are rejected. """ def pUser(self,command): """ """ if self.request_parameter == "ftp" or self.request_parameter == "anonymous": return FTP_REQ_ACCEPT return FTP_REQ_REJECT def config(self): """ Configuration for FtpProxyAnonRO It enables a minimal set of commands for a working Anonymous FTP proxy, and sets permit_unknown_commands to FALSE. """ AbstractFtpProxy.loadMinimalCommands(self) self.request["APPE"] = (FTP_REQ_ACCEPT) self.request["DELE"] = (FTP_REQ_ACCEPT) self.request["MKD"] = (FTP_REQ_ACCEPT) self.request["RMD"] = (FTP_REQ_ACCEPT) self.request["RNFR"] = (FTP_REQ_ACCEPT) self.request["RNTO"] = (FTP_REQ_ACCEPT) self.request["STOR"] = (FTP_REQ_ACCEPT) self.request["STOU"] = (FTP_REQ_ACCEPT) self.request["USER"] = (FTP_REQ_POLICY, self.pUser) self.request["*"] = (FTP_REQ_REJECT) AbstractFtpProxy.loadAnswers(self) self.response["*","*"] = (FTP_RSP_REJECT) self.features["*"] = (FTP_FEATURE_ACCEPT) self.permit_unknown_command = FALSE class FtpProxyRW(AbstractFtpProxy): """ FTP proxy based on AbstractFtpProxy, allowing full read-write access to any user. FTP proxy based on AbstractFtpProxy, enabling full read-write access to any user. Commands and return codes are strictly checked, unknown commands and responses are rejected. Every feature is accepted. The ABOR; ACCT; AUTH; CDUP; CWD; EPRT; EPSV; FEAT; LIST; MODE; MDTM; NLST; NOOP; PASV; PASS; PORT; PWD; QUIT; REST; RETR; SIZE; STAT; STRU; SYST; TYPE; and USER commands are permitted, the CLNT; XPWD; MACB; OPTS commands are rejected. """ def config(self): """ Configuration for FtpProxyRW It enables a minimal set of commands for a working FTP proxy, and sets permit_unknown_commands to FALSE. """ AbstractFtpProxy.loadMinimalCommands(self) self.request["APPE"] = (FTP_REQ_ACCEPT) self.request["DELE"] = (FTP_REQ_ACCEPT) self.request["MKD"] = (FTP_REQ_ACCEPT) self.request["RMD"] = (FTP_REQ_ACCEPT) self.request["RNFR"] = (FTP_REQ_ACCEPT) self.request["RNTO"] = (FTP_REQ_ACCEPT) self.request["STOR"] = (FTP_REQ_ACCEPT) self.request["STOU"] = (FTP_REQ_ACCEPT) self.request["USER"] = (FTP_REQ_ACCEPT) self.request["ALLO"] = (FTP_REQ_ACCEPT) self.request["*"] = (FTP_REQ_REJECT) AbstractFtpProxy.loadAnswers(self) self.response["*","*"] = (FTP_RSP_REJECT) self.features["*"] = (FTP_FEATURE_ACCEPT) self.permit_unknown_command = FALSE class FtpProxyMinimal(FtpProxyRO): """ Alias FtpProxyRO """ pass zorp-3.9.5/modules/ftp/Makefile.am000066400000000000000000000005261172670260400170270ustar00rootroot00000000000000SUBDIRS = tests pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Ftp.py pkglib_LTLIBRARIES = libftp.la libftp_la_SOURCES = ftp.c ftphash.c ftpmsg.c ftpcmd.c ftpolicy.c ftpdata.c ftp.h ftpcmd.h ftphash.h ftpolicy.h ftpmsg.h EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/ftp/ftp.c000066400000000000000000002240061172670260400157310ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "ftp.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FIXMEE * Correcting the read and write error handling */ #define SIDE_TO_STRING(side) side == EP_CLIENT ? "client" : side == EP_SERVER ? "server" : "unknown" GHashTable *ftp_command_hash = NULL; GHashTable *ftp_answer_hash = NULL; void ftp_data_reset(FtpProxy *self); gboolean ftp_data_abort(FtpProxy *self); gboolean ftp_stream_write(FtpProxy *self, char side, const guchar *line, guint length); void ftp_proxy_free(ZObject *s); void ftp_command_reject(FtpProxy *self); gboolean ftp_answer_write(FtpProxy *self, gchar *msg); gboolean ftp_command_write_setup(FtpProxy *self, gchar *answer_c, gchar *answer_p); gboolean ftp_command_write(FtpProxy *self, char *msg); static gboolean ftp_connect_server_event(FtpProxy *self, gchar *hostname, guint port) { ZSockAddr *client_local, *server_local; gchar tmpip[16]; z_proxy_enter(self); if (!z_proxy_connect_server(&self->super, hostname, port)) { /* There's no need to log, because we trust, that the called * funcion log in every cases. */ z_proxy_return(self, FALSE); } /* This must be after connect server event because we want to wait for all router, and NATs to change addresses. We set up the address pools, where we link to connect and accept sockaddrs. */ if (!z_proxy_get_addresses(&self->super, NULL, NULL, &client_local, NULL, &server_local, NULL)) z_proxy_return(self, FALSE); /* This cannot hapen, because we connected in each side */ g_assert(client_local != NULL && server_local != NULL); z_inet_ntoa(tmpip, sizeof(tmpip), ((struct sockaddr_in *) &client_local->sa)->sin_addr); self->server_port = ntohs(((struct sockaddr_in *) &client_local->sa)->sin_port); if (self->data_port_min && self->data_port_max) self->data_local_buf[EP_CLIENT] = z_sockaddr_inet_range_new(tmpip, self->data_port_min, self->data_port_max); else self->data_local_buf[EP_CLIENT] = z_sockaddr_inet_new(tmpip, 0); g_assert(self->data_local_buf[EP_CLIENT]); z_inet_ntoa(tmpip, sizeof(tmpip), ((struct sockaddr_in *) &server_local->sa)->sin_addr); if (self->data_port_min != 0 && self->data_port_max != 0) self->data_local_buf[EP_SERVER] = z_sockaddr_inet_range_new(tmpip, self->data_port_min, self->data_port_max); else self->data_local_buf[EP_SERVER] = z_sockaddr_inet_new(tmpip, 0); g_assert(self->data_local_buf[EP_SERVER]); z_sockaddr_unref(client_local); z_sockaddr_unref(server_local); z_proxy_return(self, TRUE); } void ftp_state_set(FtpProxy *self, guint order) { z_proxy_enter(self); z_stream_set_cond(self->super.endpoints[order], G_IO_IN, TRUE); z_stream_set_cond(self->super.endpoints[1 - order], G_IO_IN, FALSE); z_proxy_return(self); } void ftp_state_both(FtpProxy *self) { z_proxy_enter(self); z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, TRUE); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, TRUE); z_proxy_return(self); } static gboolean ftp_data_client_accepted(ZConnection *conn, gpointer user_data) { FtpProxy *self = (FtpProxy *) user_data; z_proxy_enter(self); if (self->data_stream[EP_CLIENT] || self->data_state == FTP_DATA_CANCEL) z_proxy_return(self, FALSE); g_mutex_lock(self->lock); if (conn && conn->stream) { gchar tmp_sockaddr[120]; /*LOG This message reports that the data connection is accepted from the client. */ z_proxy_log(self, FTP_SESSION, 5, "Data connection accepted on client side; address='%s'", z_sockaddr_format(conn->remote, tmp_sockaddr, sizeof(tmp_sockaddr))); self->data_stream[EP_CLIENT] = z_stream_ref(conn->stream); self->data_remote[EP_CLIENT] = z_sockaddr_ref(conn->remote); self->data_state |= FTP_DATA_CLIENT_READY; } else { self->data_stream[EP_CLIENT] = NULL; self->data_remote[EP_CLIENT] = NULL; self->data_state = FTP_DATA_CANCEL; self->state = FTP_QUIT; self->ftp_data_hangup = TRUE; } if (conn) z_connection_destroy(conn, FALSE); g_mutex_unlock(self->lock); z_poll_wakeup(self->poll); z_proxy_return(self, TRUE); } static gboolean ftp_data_server_accepted(ZConnection *conn, gpointer user_data) { FtpProxy *self = (FtpProxy *) user_data; z_proxy_enter(self); if (self->data_stream[EP_SERVER] || self->data_state == FTP_DATA_CANCEL) z_proxy_return(self, FALSE); g_mutex_lock(self->lock); if (conn && conn->stream) { gchar tmp_sockaddr[120]; /*LOG This message reports that the data connection is accepted from the server. */ z_proxy_log(self, FTP_SESSION, 5, "Data connection accepted on server side; address='%s'", z_sockaddr_format(conn->remote, tmp_sockaddr, sizeof(tmp_sockaddr))); self->data_stream[EP_SERVER] = z_stream_ref(conn->stream); self->data_remote[EP_SERVER] = z_sockaddr_ref(conn->remote); self->data_state |= FTP_DATA_SERVER_READY; } else { self->data_stream[EP_SERVER] = NULL; self->data_remote[EP_SERVER] = NULL; self->data_state = FTP_DATA_CANCEL; self->state = FTP_QUIT; self->ftp_data_hangup = TRUE; } if (conn) z_connection_destroy(conn, FALSE); g_mutex_unlock(self->lock); z_poll_wakeup(self->poll); z_proxy_return(self, TRUE); } void ftp_data_client_connected(ZConnection *conn, gpointer user_data) { FtpProxy *self = (FtpProxy *) user_data; z_proxy_enter(self); g_mutex_lock(self->lock); if (!(self->data_state & FTP_DATA_CLIENT_READY) && self->data_state != FTP_DATA_CANCEL && self->data_state != FTP_DATA_DESTROY) { if (conn && conn->stream) { gchar tmp_sockaddr[120]; /*LOG This message reports that the data connection is established to the client. */ z_proxy_log(self, FTP_SESSION, 5, "Data connection established on client side; address='%s'", z_sockaddr_format(conn->remote, tmp_sockaddr, sizeof(tmp_sockaddr))); self->data_stream[EP_CLIENT] = z_stream_ref(conn->stream); z_sockaddr_unref(self->data_remote[EP_CLIENT]); self->data_remote[EP_CLIENT] = z_sockaddr_ref(conn->remote); self->data_state |= FTP_DATA_CLIENT_READY; } else { /* We assume that lower level log if problem occured */ self->data_state = FTP_DATA_DESTROY; self->state = FTP_QUIT; self->ftp_data_hangup = TRUE; } if (conn) { z_connection_destroy(conn, FALSE); conn = NULL; } z_poll_wakeup(self->poll); } g_mutex_unlock(self->lock); if (conn) { /*LOG This message indicates that the data connection is established to the client, but the no connection is expected or the connection is canceled meanwhile. */ z_proxy_log(self, FTP_ERROR, 4, "Connected to client, but connection is not expected; state='%ld'", self->data_state); z_connection_destroy(conn, TRUE); } z_proxy_return(self); } void ftp_data_server_connected(ZConnection *conn, gpointer user_data) { FtpProxy *self = (FtpProxy *) user_data; z_proxy_enter(self); g_mutex_lock(self->lock); if (!(self->data_state & FTP_DATA_SERVER_READY) && self->data_state != FTP_DATA_CANCEL && self->data_state != FTP_DATA_DESTROY) { if (conn && conn->stream) { gchar tmp_sockaddr[120]; /*LOG This message reports that the data connection is established to the server. */ z_proxy_log(self, FTP_SESSION, 5, "Data connection established on server side; address='%s'", z_sockaddr_format(conn->remote, tmp_sockaddr, sizeof(tmp_sockaddr))); self->data_stream[EP_SERVER] = z_stream_ref(conn->stream); z_sockaddr_unref(self->data_remote[EP_SERVER]); self->data_remote[EP_SERVER] = z_sockaddr_ref(conn->remote); self->data_state |= FTP_DATA_SERVER_READY; } else { /* We assume that lower level log, if problem occured */ self->data_state = FTP_DATA_DESTROY; self->state = FTP_SERVER_TO_CLIENT; /* not considered fatal */ } z_poll_wakeup(self->poll); if (conn) { z_connection_destroy(conn, FALSE); conn = NULL; } } g_mutex_unlock(self->lock); if (conn) { /*LOG This message indicates that the data connection is established to the server, but the no connection is expected or the connection is canceled meanwhile. */ z_proxy_log(self, FTP_ERROR, 4, "Connected to server, but connection is not expected; state='%ld'", self->data_state); z_connection_destroy(conn, TRUE); } z_proxy_return(self); } ZAttachCallbackFunc data_attach_callbacks[] = { ftp_data_client_connected, ftp_data_server_connected }; ZDispatchCallbackFunc data_accept_callbacks[] = { ftp_data_client_accepted, ftp_data_server_accepted }; gboolean ftp_data_prepare(FtpProxy *self, gint side, gchar mode) { ZDispatchParams dpparam; ZDispatchBind *db; ZAttachParams aparam; ZSockAddr *tmpaddr; gchar tmpip[16]; z_proxy_enter(self); /* FIXMEE Correct handling of not freed streams! */ self->data_stream[side] = NULL; if (mode == 'L') { memset(&dpparam, 0, sizeof(dpparam)); dpparam.tcp.accept_one = FALSE; dpparam.tcp.backlog = 1; dpparam.common.mark_tproxy = TRUE; z_proxy_ref(&self->super); if (self->data_listen[side] != NULL) { /*LOG This message indicates that the previous data connection was not completely teared down and a new one is about to accept. This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 3, "Internal error, previous dispatcher not unregistered; side='%s', mode='%c'", SIDE_TO_STRING(side), mode); z_dispatch_unregister(self->data_listen[side]); } db = z_dispatch_bind_new_sa(ZD_PROTO_TCP, self->data_local_buf[side]); self->data_listen[side] = z_dispatch_register(self->super.session_id, db, &tmpaddr, ZD_PRI_RELATED, &dpparam, data_accept_callbacks[side], self, (GDestroyNotify)z_proxy_unref); z_dispatch_bind_unref(db); if (!self->data_listen[side]) { /* We assume that lower level log if problem occured */ z_proxy_unref(&self->super); z_proxy_return(self, FALSE); } self->data_local[side] = tmpaddr; if (self->data_connect[side]) { /*LOG This message indicates that the previous data connection was not completely teared down while a new connection is being established. This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 3, "Internal error, previous attach not unregistered; side='%s', mode='%c'", SIDE_TO_STRING(side), mode); z_attach_cancel(self->data_connect[side]); z_attach_free(self->data_connect[side]); self->data_connect[side] = NULL; } } else if (mode == 'C') { if (side == EP_CLIENT) { guint port; tmpaddr = self->data_local_buf[side]; z_inet_ntoa(tmpip, sizeof(tmpip), ((struct sockaddr_in *) &tmpaddr->sa)->sin_addr); switch (self->active_connection_mode) { case FTP_ACTIVE_TWENTY: port = 20; break; case FTP_ACTIVE_RANDOM: port = 0; break; case FTP_ACTIVE_MINUSONE: default: port = self->server_port - 1; } self->data_local[side] = z_sockaddr_inet_new(tmpip, port); } else { self->data_local[side] = z_sockaddr_ref(self->data_local_buf[side]); } memset(&aparam, 0, sizeof(aparam)); aparam.timeout = -1; if (self->data_connect[side] != NULL) { /*LOG This message indicates that the previous data connection was not completely teared down and a new one is being established. This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 3, "Internal error, previous attach not unregistered; side='%s', mode='%c'", SIDE_TO_STRING(side), mode); z_attach_cancel(self->data_connect[side]); z_attach_free(self->data_connect[side]); } z_proxy_ref(&self->super); self->data_connect[side] = z_attach_new(&self->super, ZD_PROTO_TCP, self->data_local[side], self->data_remote[side], &aparam, data_attach_callbacks[side], self, (GDestroyNotify) z_proxy_unref); z_sockaddr_unref(self->data_local[side]); self->data_local[side] = NULL; if (!self->data_connect[side]) { /* We assume that lower level log if problem accured */ z_proxy_unref(&self->super); z_proxy_leave(self); return FALSE; } if (self->data_listen[side]) { /*LOG This message indicates that the previous data connection was not completely teared down and a new one is about to accept. This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 3, "Internal error, previous dispatcher not unregistered; side='%s', mode='%c'", SIDE_TO_STRING(side), mode); z_dispatch_unregister(self->data_listen[side]); self->data_listen[side] = NULL; } } z_proxy_return(self, TRUE); } void ftp_data_reset(FtpProxy *self) { z_proxy_enter(self); /*LOG This message indicates that the data connection is going to be destroyed. */ z_proxy_log(self, FTP_DEBUG, 6, "Resetting data connection; state='%d', oldstate='%d'", self->state, self->oldstate); if (self->data_connect[EP_CLIENT]) { z_attach_cancel(self->data_connect[EP_CLIENT]); z_attach_free(self->data_connect[EP_CLIENT]); self->data_connect[EP_CLIENT] = NULL; } if (self->data_connect[EP_SERVER]) { z_attach_cancel(self->data_connect[EP_SERVER]); z_attach_free(self->data_connect[EP_SERVER]); self->data_connect[EP_SERVER] = NULL; } if (self->data_listen[EP_CLIENT]) { z_dispatch_unregister(self->data_listen[EP_CLIENT]); self->data_listen[EP_CLIENT] = NULL; } if (self->data_listen[EP_SERVER]) { z_dispatch_unregister(self->data_listen[EP_SERVER]); self->data_listen[EP_SERVER] = NULL; } if (self->data_stream[EP_CLIENT]) { z_stream_shutdown(self->data_stream[EP_CLIENT], SHUT_RDWR, NULL); z_stream_close(self->data_stream[EP_CLIENT], NULL); z_stream_unref(self->data_stream[EP_CLIENT]); self->data_stream[EP_CLIENT] = NULL; } if (self->data_stream[EP_SERVER]) { z_stream_shutdown(self->data_stream[EP_SERVER], SHUT_RDWR, NULL); z_stream_close(self->data_stream[EP_SERVER], NULL); z_stream_unref(self->data_stream[EP_SERVER]); self->data_stream[EP_SERVER] = NULL; } g_mutex_lock(self->lock); if (self->data_remote[EP_CLIENT]) { z_sockaddr_unref(self->data_remote[EP_CLIENT]); self->data_remote[EP_CLIENT] = NULL; } if (self->data_remote[EP_SERVER]) { z_sockaddr_unref(self->data_remote[EP_SERVER]); self->data_remote[EP_SERVER] = NULL; } if (self->data_local[EP_CLIENT]) { z_sockaddr_unref(self->data_local[EP_CLIENT]); self->data_local[EP_CLIENT] = NULL; } if (self->data_local[EP_SERVER]) { z_sockaddr_unref(self->data_local[EP_SERVER]); self->data_local[EP_SERVER] = NULL; } self->data_state = 0; g_mutex_unlock(self->lock); if (self->transfer) { z_transfer2_cancel(self->transfer); self->transfer = NULL; } z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_PRI, FALSE); if (self->ftp_data_hangup) { ftp_answer_write(self, "421 Logoff"); self->ftp_data_hangup = FALSE; } if (self->preamble) { g_free(self->preamble); self->preamble = NULL; } if (self->state != FTP_QUIT) { switch(self->oldstate) { case FTP_SERVER_TO_CLIENT: ftp_state_set(self, EP_SERVER); self->state = self->oldstate; break; case FTP_CLIENT_TO_SERVER: ftp_state_set(self, EP_CLIENT); self->state = self->oldstate; break; default: break; } } self->oldstate = 0; z_proxy_return(self); } void ftp_data_start(FtpProxy *self) { z_proxy_enter(self); if (self->data_state & FTP_DATA_COMMAND_START) { /*LOG This message indicates an internal error that a previous data connection was not completed successfully. This condition should not occur, though the event is harmless, an explicit data reset clears this state. Please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 3, "Internal error, previous data connection is not closed properly; data_state='%lx", self->data_state); ftp_data_reset(self); } self->data_state |= FTP_DATA_COMMAND_START; z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, FALSE); z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_PRI, TRUE); z_proxy_return(self); } static gboolean ftp_data_do_ssl_handshake(FtpProxy *self, gint side) { gboolean rc = TRUE; z_proxy_enter(self); /* require SSL if: * - we're using FTPS and the protection level is private * - the proxy requires SSL on this side */ if ((self->data_protection_enabled[side]) || self->super.ssl_opts.security[side] == PROXY_SSL_SEC_FORCE_SSL) { ZProxySSLHandshake *handshake; ZStream *old; /* push an SSL stream on */ old = self->data_stream[side]; self->data_stream[side] = z_stream_ssl_new(old, NULL); z_stream_unref(old); /* do handshake */ handshake = z_proxy_ssl_handshake_new(&self->super, self->data_stream[side], side); rc = z_proxy_ssl_perform_handshake(handshake); if (!handshake->session) rc = FALSE; } z_proxy_return(self, rc); } static gboolean ftp_data_create_transfer(FtpProxy *self) { gboolean rc = TRUE; ZStream *streams[EP_MAX]; gint first_side; gchar tmpsaddr_client[120]; gchar tmpsaddr_server[120]; z_proxy_enter(self); if (!self->data_stream[EP_CLIENT] || !self->data_stream[EP_SERVER]) { /*LOG This message indicates an internal error that the proxy is unable to start a stacked data proxy because either the data connection to the FTP client or the FTP server is not yet established. Please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 2, "Internal error, cannot start data proxy because peers are not yet connected;"); z_proxy_return(self, FALSE); } /* do SSL handshake if required */ if (self->super.ssl_opts.handshake_seq == PROXY_SSL_HS_CLIENT_SERVER) first_side = EP_CLIENT; else first_side = EP_SERVER; rc = ftp_data_do_ssl_handshake(self, first_side); if (rc) rc = ftp_data_do_ssl_handshake(self, EP_OTHER(first_side)); if (!rc) { z_proxy_log(self, FTP_ERROR, 3, "SSL handshake failed on data connection, cannot start transfer;"); z_proxy_return(self, FALSE); } if (self->command_desc && self->command_desc->need_data == 2) /* data: cli -> svr */ { streams[EP_CLIENT] = self->data_stream[EP_CLIENT]; streams[EP_SERVER] = self->data_stream[EP_SERVER]; } else if (self->command_desc && self->command_desc->need_data == 1) /* data: svr -> cli */ { streams[EP_CLIENT] = self->data_stream[EP_SERVER]; streams[EP_SERVER] = self->data_stream[EP_CLIENT]; } else { /*LOG This message indicates an internal error that the current command descriptor changed while the data connection was being set up, thus we are unable to know which direction is permitted during data transfer. Please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 2, "Internal error, current command descriptor does not specify data transfer;"); z_proxy_leave(self); return FALSE; } self->data_stream[EP_SERVER] = NULL; self->data_stream[EP_CLIENT] = NULL; /*LOG This message reports that data connection is established between the client and the server, and proxy is being stacked for the data transfer. */ z_proxy_log(self, FTP_SESSION, 4, "Data connection established; client_type='%s', client_address='%s', server_type='%s', server_address='%s', flow='%s'", self->data_listen[EP_CLIENT] ? "passive" : "active", z_sockaddr_format(self->data_remote[EP_CLIENT], tmpsaddr_client, sizeof(tmpsaddr_client)), self->data_connect[EP_SERVER] ? "passive" : "active", z_sockaddr_format(self->data_remote[EP_SERVER], tmpsaddr_server, sizeof(tmpsaddr_server)), self->command_desc->need_data == 1 ? "download" : self->command_desc->need_data == 2 ? "upload" : "unknown"); if (!ftp_data_transfer(self, streams[EP_CLIENT], streams[EP_SERVER])) { ftp_command_reject(self); self->drop_answer = TRUE; } ftp_data_reset(self); z_proxy_return(self, rc); } static void ftp_data_next_step(FtpProxy *self) { gchar buf[4096]; z_proxy_enter(self); g_mutex_lock(self->lock); z_proxy_trace(self, "data_state = '%lx'", self->data_state); if ((self->data_state & FTP_DATA_COMMAND_START) && !(self->data_state & FTP_DATA_SERVER_START)) { z_proxy_cp(self); if (self->data_connect[EP_SERVER]) { if (ftp_policy_bounce_check(self, EP_SERVER, self->data_remote[EP_SERVER], TRUE)) { if (!z_attach_start(self->data_connect[EP_SERVER], NULL, &self->data_local[EP_SERVER])) { /* NOTE: We assume here, that lower level log if problem occured */ g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_leave(self); return; } } else { /*LOG This message indicates that the IP address of the data connection to be established differs from the IP of the control connection. This might be caused by a real bounce attack on FTP, or a erroneously configured NAT translation on the client or server side. */ z_proxy_log(self, FTP_POLICY, 3, "Possible bounce attack; connect='TRUE', side='server', remote='%s'", z_sockaddr_format(self->data_remote[EP_SERVER], buf, sizeof(buf))); g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_return(self); } } self->data_state |= FTP_DATA_SERVER_START; } if ((self->data_state & FTP_SERVER_FIRST_READY) == FTP_SERVER_FIRST_READY && !(self->data_state & FTP_DATA_CLIENT_START)) { z_proxy_cp(self); if (!self->data_connect[EP_SERVER]) { if (!ftp_policy_bounce_check(self, EP_SERVER, self->data_remote[EP_SERVER], FALSE)) { /*LOG This message indicates that the IP address of the data connection to be established differs from the IP of the control connection. This might be caused by a real bounce attack on FTP, or a erroneously configured NAT translation on the client or server side. */ z_proxy_log(self, FTP_POLICY, 3, "Possible bounce attack; connect='FALSE', side='server', remote='%s'", z_sockaddr_format(self->data_remote[EP_SERVER], buf, sizeof(buf))); g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_leave(self); return; } } z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, FALSE); self->data_state |= FTP_DATA_CLIENT_START; if (self->data_connect[EP_CLIENT]) { if (ftp_policy_bounce_check(self, EP_CLIENT, self->data_remote[EP_CLIENT], TRUE)) { if (!z_attach_start(self->data_connect[EP_CLIENT], NULL, &self->data_local[EP_CLIENT])) { /* NOTE: We assume here, that lower level log if problem occured */ g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_leave(self); return; } } else { /*LOG This message indicates that the IP address of the data connection to be established differs from the IP of the control connection. This might be caused by a real bounce attack on FTP, or a erroneously configured NAT translation on the client or server side. */ z_proxy_log(self, FTP_POLICY, 3, "Possible bounce attack; connect='TRUE', side='client', remote='%s'", z_sockaddr_format(self->data_remote[EP_CLIENT], buf, sizeof(buf))); g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_return(self); } } } if (self->data_state == FTP_SERVER_CONNECT_READY) { if (!self->data_connect[EP_CLIENT]) { if (!ftp_policy_bounce_check(self, EP_CLIENT, self->data_remote[EP_CLIENT], FALSE)) { /*LOG This message indicates that the IP address of the data connection to be established differs from the IP of the control connection. This might be caused by a real bounce attack on FTP, or a erroneously configured NAT translation on the client or server side. */ z_proxy_log(self, FTP_POLICY, 3, "Possible bounce attack; connect='FALSE', side='client', remote='%s'", z_sockaddr_format(self->data_remote[EP_CLIENT], buf, sizeof(buf))); g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_return(self); } } g_mutex_unlock(self->lock); if (!ftp_data_create_transfer(self)) ftp_data_reset(self); z_proxy_return(self); } else if (self->data_state == FTP_DATA_CANCEL) { g_mutex_unlock(self->lock); ftp_data_reset(self); z_proxy_return(self); } else if (self->data_state == FTP_DATA_DESTROY) { /* FIXME * Correcly handling if one of the connection closed (server/client) */ g_mutex_unlock(self->lock); ftp_data_reset(self); // ftp_data_abort(self); // SET_ANSWER(MSG_INVALID_PARAMETER); // self->state = FTP_QUIT; // ftp_command_reject(self); z_proxy_return(self); } g_mutex_unlock(self->lock); z_proxy_return(self); } gboolean ftp_data_abort(FtpProxy *self) { char buf[3]; gsize len; GIOStatus rc; z_proxy_enter(self); ftp_data_reset(self); buf[0]=0xff; buf[1]=0xf4; buf[2]=0xff; rc = z_stream_write_pri(self->super.endpoints[EP_SERVER], buf, 3, &len, NULL); if (rc == G_IO_STATUS_NORMAL) { buf[0]=0xf2; rc = z_stream_write(self->super.endpoints[EP_SERVER], buf, 1, &len, NULL); ftp_stream_write(self, 'S', (const guchar *)"ABOR", 4); } z_proxy_return(self, rc == G_IO_STATUS_NORMAL); } static gboolean ftp_client_data(ZStream *stream, GIOCondition cond, gpointer user_data); static gboolean ftp_server_data(ZStream *stream, GIOCondition cond, gpointer user_data); gboolean ftp_stream_client_init(FtpProxy *self) { ZStream *tmpstream; z_proxy_enter(self); if (!self->super.endpoints[EP_CLIENT]) { /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, client side not connected;"); z_proxy_return(self, FALSE); } self->super.endpoints[EP_CLIENT]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_CLIENT]; self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, FALSE); z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_OUT, FALSE); z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_PRI, FALSE); z_stream_set_callback(self->super.endpoints[EP_CLIENT], G_IO_IN, ftp_client_data, self, NULL); z_stream_set_callback(self->super.endpoints[EP_CLIENT], G_IO_PRI, ftp_client_data, self, NULL); z_poll_add_stream(self->poll, self->super.endpoints[EP_CLIENT]); z_proxy_return(self, TRUE); } gboolean ftp_stream_server_init(FtpProxy *self) { ZStream *tmpstream; z_proxy_enter(self); if (!self->super.endpoints[EP_SERVER]) { /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, server side not connected;"); z_proxy_return(self, FALSE); } self->super.endpoints[EP_SERVER]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_SERVER]; self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, FALSE); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_OUT, FALSE); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_PRI, FALSE); z_stream_set_callback(self->super.endpoints[EP_SERVER], G_IO_IN, ftp_server_data, self, NULL); z_poll_add_stream(self->poll, self->super.endpoints[EP_SERVER]); z_proxy_return(self, TRUE); } static void ftp_deinit_streams(FtpProxy *self) { if (self->super.endpoints[EP_CLIENT]) z_poll_remove_stream(self->poll, self->super.endpoints[EP_CLIENT]); if (self->super.endpoints[EP_SERVER]) z_poll_remove_stream(self->poll, self->super.endpoints[EP_SERVER]); } void ftp_proxy_regvars(FtpProxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "transparent_mode", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->transparent_mode); z_proxy_var_new(&self->super, "permit_unknown_command", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->permit_unknown_command); z_proxy_var_new(&self->super, "permit_empty_command", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->permit_empty_command); z_proxy_var_new(&self->super, "max_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_line_length); z_proxy_var_new(&self->super, "max_username_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_username_length); z_proxy_var_new(&self->super, "max_password_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_password_length); z_proxy_var_new(&self->super, "max_hostname_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_hostname_length); z_proxy_var_new(&self->super, "masq_address_client", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG, self->masq_address[EP_CLIENT]); z_proxy_var_new(&self->super, "masq_address_server", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG, self->masq_address[EP_SERVER]); z_proxy_var_new(&self->super, "request", Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->policy_command_hash); z_proxy_var_new(&self->super, "commands", Z_VAR_TYPE_OBSOLETE | Z_VAR_GET | Z_VAR_GET_CONFIG, "request"); z_proxy_var_new(&self->super, "response", Z_VAR_TYPE_DIMHASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->policy_answer_hash); z_proxy_var_new(&self->super, "answers", Z_VAR_TYPE_OBSOLETE | Z_VAR_GET | Z_VAR_GET_CONFIG, "response"); z_proxy_var_new(&self->super, "request_command", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->request_cmd); z_proxy_var_new(&self->super, "request_parameter", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->request_param); z_proxy_var_new(&self->super, "response_status", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->answer_cmd); z_proxy_var_new(&self->super, "answer_code", Z_VAR_TYPE_OBSOLETE | Z_VAR_GET | Z_VAR_SET, "response_status"); z_proxy_var_new(&self->super, "response_parameter", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->answer_param); z_proxy_var_new(&self->super, "answer_parameter", Z_VAR_TYPE_OBSOLETE | Z_VAR_GET | Z_VAR_SET, "response_param"); z_proxy_var_new(&self->super, "data_mode", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->data_mode); z_proxy_var_new(&self->super, "username", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->username); z_proxy_var_new(&self->super, "password", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->password); z_proxy_var_new(&self->super, "hostname", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->hostname); z_proxy_var_new(&self->super, "hostport", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET, &self->hostport); z_proxy_var_new(&self->super, "proxy_username", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->proxy_username); z_proxy_var_new(&self->super, "proxy_password", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->proxy_password); z_proxy_var_new(&self->super, "proxy_auth_needed", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET, &self->proxy_auth_needed); z_proxy_var_new(&self->super, "auth", Z_VAR_TYPE_OBJECT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->auth); z_proxy_var_new(&self->super, "response_strip_msg", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->response_strip_msg); z_proxy_var_new(&self->super, "timeout", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->timeout); z_proxy_var_new(&self->super, "buffer_size", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, &self->buffer_size); z_proxy_var_new(&self->super, "target_port_range", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG, self->target_port_range); z_proxy_var_new(&self->super, "data_port_min", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->data_port_min); z_proxy_var_new(&self->super, "data_port_max", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->data_port_max); z_proxy_var_new(&self->super, "valid_chars_username", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG, self->valid_chars_username); z_proxy_var_new(&self->super, "active_connection_mode", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->active_connection_mode); z_proxy_var_new(&self->super, "max_continuous_line", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_continuous_line); z_proxy_var_new(&self->super, "features", Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->policy_features); z_proxy_return(self); } void ftp_config_set_defaults(FtpProxy * self) { z_proxy_enter(self); self->ftp_state = FTP_STATE_CONNECT; self->transparent_mode = TRUE; self->permit_empty_command = TRUE; self->permit_unknown_command = FALSE; self->response_strip_msg = FALSE; self->max_line_length = 255; self->max_username_length = 32; self->max_password_length = 64; self->max_hostname_length = 128; self->line = g_new0(char, FTP_LINE_MAX_LEN + 1); self->username = g_string_new(""); self->password = g_string_new(""); self->hostname = g_string_new(""); self->proxy_username = g_string_new(""); self->proxy_password = g_string_new(""); self->proxy_auth_needed = 0; self->auth_done = FALSE; self->data_mode = FTP_DATA_KEEP; self->masq_address[EP_SERVER] = g_string_new(""); self->masq_address[EP_CLIENT] = g_string_new(""); self->lock = g_mutex_new(); self->timeout = 300000; self->max_continuous_line = 100; self->policy_command_hash = ftp_policy_command_hash_create(); self->policy_answer_hash = ftp_policy_answer_hash_create(); self->policy_features = g_hash_table_new(g_str_hash, g_str_equal); self->request_cmd = g_string_sized_new(4); self->request_param = g_string_new(""); self->answer_cmd = g_string_sized_new(4); self->answer_param = g_string_new(""); self->target_port_range = g_string_new("21"); self->hostport = 21; self->poll = z_poll_new(); self->data_port_min = 40000; self->data_port_max = 41000; self->auth_tls_ok[EP_CLIENT] = FALSE; self->auth_tls_ok[EP_SERVER] = FALSE; self->data_protection_enabled[EP_CLIENT] = FALSE; self->data_protection_enabled[EP_SERVER] = FALSE; self->valid_chars_username = g_string_new("a-zA-Z0-9._@"); self->buffer_size = 4096; z_proxy_return(self); } gboolean ftp_config_init(FtpProxy *self) { z_proxy_enter(self); if (self->max_line_length > FTP_LINE_MAX_LEN) { /*LOG This message indicates that the configured max_line_length is above upper limit and Zorp sets it to the upper limit. */ z_proxy_log(self, FTP_POLICY, 2, "Max_line_length above upper limit; max_line_length='%d', upper_limit='%d'", self->max_line_length, FTP_LINE_MAX_LEN); self->max_line_length = FTP_LINE_MAX_LEN; } if (self->max_username_length > self->max_line_length) { /*LOG This message indicates that the configured max_username_length is above max_line_length which does not make sense and Zorp sets it to the max_line_length. */ z_proxy_log(self, FTP_POLICY, 2, "Max_username_length above max_line_length; max_username_length='%d', max_line_length='%d'", self->max_username_length, self->max_line_length); self->max_username_length = self->max_line_length; } if (self->max_password_length > self->max_line_length) { /*LOG This message indicates that the configured max_password_length is above max_line_length which does not make sense and Zorp sets it to the max_line_length. */ z_proxy_log(self, FTP_POLICY, 2, "Max_password_length above max_line_length; max_password_length='%d', max_line_length='%d'", self->max_password_length, self->max_line_length); self->max_password_length = self->max_line_length; } if (self->max_hostname_length > self->max_line_length) { /*LOG This message indicates that the configured max_hostname_length is above max_line_length which does not make sense and Zorp sets it to the max_line_length. */ z_proxy_log(self, FTP_POLICY, 2, "Max_hostname_length above max_line_length; max_hostname_length='%d', max_line_length='%d'", self->max_hostname_length, self->max_line_length); self->max_hostname_length = self->max_line_length; } if (!z_charset_parse(&self->username_charset, self->valid_chars_username->str)) { /*LOG This message indicates that the character set specified in the valid_chars_username attribute has a syntax error. */ z_proxy_log(self, FTP_POLICY, 2, "Error parsing valid_chars_username; value='%s'", self->valid_chars_username->str); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } GIOStatus ftp_read_line_get (FtpProxy * self, guint side, gint *error_value) { gint readback = G_IO_STATUS_ERROR; guint i; gint state; char *tmp; guint pos1; unsigned char funcs[10] = { 241, 242, 243, 244, 245, 246, 247, 248, 249, '\0' }; unsigned char negot[5] = { 251, 252, 253, 254, '\0' }; z_proxy_enter(self); self->line_length = self->max_line_length; readback = z_stream_line_get_copy(self->super.endpoints[side], self->line, &self->line_length, NULL); *error_value = errno; if (readback != G_IO_STATUS_NORMAL) { /* NOTE Here we assume that lower level log if problem occured */ self->line_length = 0; z_proxy_return(self, readback); } tmp = g_new0(char, (self->line_length) + 2); state = FTP_TELNET; pos1 = 0; if (self->line_length) for (i = 0; i < self->line_length; i++) { // TODO: adding SB SE switch (state) { case FTP_TELNET: if ((unsigned char) self->line[i] != 255) { tmp[pos1++] = self->line[i]; } else { if ((unsigned char) self->line[i + 1] == 255) { tmp[pos1++] = self->line[i]; i++; } else { state = FTP_TELNET_IAC; } } break; case FTP_TELNET_IAC: if (strchr ((const char *) funcs, self->line[i])) { // in funcs state = FTP_TELNET; if ((unsigned char) self->line[i + 1] == 242) { i++; } } else if (strchr ((const char *) negot, self->line[i])) { // in negotiation state = FTP_TELNET_IAC_DW; } else if ((unsigned char) self->line[i] == 250) { state = FTP_TELNET_IAC_FUNC; } else { // Bad seq } break; case FTP_TELNET_IAC_DW: state = FTP_TELNET; break; case FTP_TELNET_IAC_FUNC: if ((unsigned char) self->line[i] == 240) state = FTP_TELNET; break; default: break; } } tmp[pos1] = 0; self->line_length = pos1; /* It's theoretically impossible to overflow. */ strcpy(self->line, tmp); g_free (tmp); z_proxy_return(self, readback); } gboolean ftp_stream_write(FtpProxy *self, char side, const guchar *line, guint length) { gsize bytes_written = 0; gchar buf[2 * length + 3]; guint i, j; GIOStatus rc; z_proxy_enter(self); for (i = 0, j = 0; i < length; i++) { buf[j++] = line[i]; if (line[i] == 255) { buf[j++] = 255; } } buf[j++] = '\r'; buf[j++] = '\n'; switch (side) { case 'S': rc = z_stream_write(self->super.endpoints[EP_SERVER], buf, j, &bytes_written, NULL); break; case 'C': rc = z_stream_write(self->super.endpoints[EP_CLIENT], buf, j, &bytes_written, NULL); break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error in stream write, side is wrong; side='%c'", side); assert(0); break; } if (bytes_written == j) z_proxy_return(self, TRUE); if (rc == G_IO_STATUS_NORMAL) { /*LOG This message reports that Zorp was unable to write out a full line and some data remained in the buffer. */ z_proxy_log(self, FTP_ERROR, 4, "Cannot write full line; remained='%.*s'", j, buf + bytes_written); } z_proxy_return(self, FALSE); } gboolean ftp_answer_parse(FtpProxy *self) { char answer[4]; int i; z_proxy_enter(self); for (i = 0; i < 3; i++) if (!isdigit(self->line[i])) { /*LOG This message indicates that the server's answer does not begin with a valid 3 character long number. */ z_proxy_log(self, FTP_VIOLATION, 1, "Server answer doesn't begin with number; line='%s'", self->line); z_proxy_leave(self); return FALSE; } else answer[i] = self->line[i]; answer[3] = '\0'; /* zero terminate answer */ g_string_assign(self->answer_cmd, answer); self->line[self->line_length] = 0; g_string_assign(self->answer_param, self->line + 4); /*LOG This message reports that a valid answer is read from the server. */ z_proxy_log(self, FTP_RESPONSE, 6, "Response arrived; rsp='%s', rsp_prm='%s'", self->answer_cmd->str, self->answer_param->str); z_proxy_leave(self); return TRUE; } gboolean ftp_answer_fetch(FtpProxy *self, gboolean *continued) { guint res; gboolean cont = FALSE; int i; gint error_value; z_proxy_enter(self); res = ftp_read_line_get(self, EP_SERVER, &error_value); if (res == G_IO_STATUS_EOF) z_proxy_return(self, FALSE); /* read EOF */ if (res != G_IO_STATUS_NORMAL) { /*LOG This message indicates that Zorp was unable to fetch the answer from the server. It is likely caused by some timeout. */ z_proxy_log(self, FTP_ERROR, 1, "Error reading from server; error='%s'", strerror(error_value)); z_proxy_return(self, FALSE); } if (*continued) { z_cp(); g_string_append_c(self->answer_param, '\n'); z_proxy_log(self, FTP_RESPONSE, 6, "Response continuation arrived; data='%s'", self->line); if(self->line_length < 4) { cont = TRUE; g_string_append_len(self->answer_param, self->line, self->line_length); } else { for (i = 0; i < 3 ; i++) { if (!isdigit(self->line[i])) { cont = TRUE; break; } } if (i == 3 && !memcmp(self->line, self->answer_cmd->str, 3) && (self->line[i] == ' ' || self->line[i] == '-')) { g_string_append_len(self->answer_param, self->line + 4, self->line_length - 4); if (self->line[i] == '-') cont = TRUE; } else { g_string_append_len(self->answer_param, self->line, self->line_length); cont = TRUE; } } } else { z_cp(); if (self->line_length < 4) { /*LOG This message indicates that too short answer is read from the server. A valid answer must be at least 4 character long. */ z_proxy_log(self, FTP_VIOLATION, 1, "Line is too short to be a valid answer; line='%s'", self->line); z_proxy_return(self, FALSE); } if (self->line[3] != ' ' && self->line[3] != '-') { /*LOG This message indicates that the server's answer has invalid continuation mark. */ z_proxy_log(self, FTP_VIOLATION, 1, "Server answer has wrong continuation mark; line='%s'", self->line); z_proxy_return(self, FALSE); } if (!ftp_answer_parse(self)) z_proxy_return(self, FALSE); } *continued = (cont || self->line[3]=='-'); z_proxy_return(self, TRUE); } void ftp_answer_process(FtpProxy *self) { FtpInternalCommand *command = self->command_desc; int res; z_proxy_enter(self); res = ftp_policy_answer_hash_do(self); self->answer_code = atoi(self->answer_cmd->str); if (res == FTP_RSP_ACCEPT) { if (command && command->answer) res = command->answer(self); } self->answer_handle = res; switch (res) { case FTP_RSP_ACCEPT: ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); break; case FTP_RSP_ABORT: self->state = FTP_QUIT; /* no break; we must write answer... */ case FTP_RSP_REJECT: /*LOG This message indicates that the given response is rejected and changed by the policy. */ z_proxy_log(self, FTP_POLICY, 3, "Rejected answer; from='%s', to='%s %s'", self->line, self->answer_cmd->str, self->answer_param->str); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); break; case FTP_NOOP: break; default: self->state = FTP_QUIT; break; } z_proxy_return(self); } gchar * ftp_answer_setup(FtpProxy *self, gchar *answer_c, gchar *answer_p) { gchar *tmp; GString *newline; z_proxy_enter(self); newline = g_string_sized_new(self->max_line_length); tmp = strchr(answer_p, '\n'); if(!tmp) { g_string_append_printf(newline, "%s %s", answer_c, answer_p); } else { gboolean first_line = TRUE; while (tmp) { *tmp = 0; if (first_line) g_string_append_printf(newline, "%s-%s\r\n", answer_c, answer_p); else g_string_append_printf(newline, " %s\r\n", answer_p); *tmp = '\n'; answer_p = tmp + 1; tmp = strchr(answer_p, '\n'); first_line = FALSE; } if (*answer_p) g_string_append_printf(newline, "%s %s", answer_c, answer_p); else g_string_append_printf(newline, "%s ", answer_c); } z_proxy_return(self, g_string_free(newline, FALSE)); } gboolean ftp_answer_write_with_setup(FtpProxy *self, gchar *answer_c, gchar *answer_p) { gchar *newline; gboolean res = TRUE; z_proxy_enter(self); newline = ftp_answer_setup(self, answer_c, answer_p); res = ftp_answer_write(self,newline); g_free(newline); z_proxy_return(self, res); } gboolean ftp_answer_write(FtpProxy *self, gchar *msg) { guint bytes_to_write; gboolean back = TRUE; z_proxy_enter(self); if (!self->drop_answer) { /* TODO PASV doesn't work!!!! */ if (self->response_strip_msg) bytes_to_write = 4; else bytes_to_write = strlen(msg); back = ftp_stream_write(self, 'C', (const guchar *) msg, bytes_to_write); } self->drop_answer = FALSE; z_proxy_return(self, back); } gboolean ftp_command_fetch(FtpProxy *self) { guint res; gint error_value; z_proxy_enter(self); res = ftp_read_line_get(self, EP_CLIENT, &error_value); if (res == G_IO_STATUS_EOF) z_proxy_return(self, FALSE); /* read EOF */ if (res != G_IO_STATUS_NORMAL) { /*LOG This message indicates that Zorp was unable to read from the client side. */ z_proxy_log(self, FTP_ERROR, 2, "Error reading from client; error='%s'", strerror(error_value)); if (errno == ETIMEDOUT) SET_ANSWER(MSG_TIMED_OUT) else SET_ANSWER(MSG_LINE_TERM_CRLF) ftp_command_reject(self); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } gboolean ftp_command_parse(FtpProxy *self) { gchar *src = self->line; guint i = 0; z_proxy_enter(self); g_string_assign(self->request_cmd, ""); while ((*src != ' ') && (i < self->line_length)) { g_string_append_c(self->request_cmd, *src++); i++; } src++; i++; if (i < self->line_length) g_string_assign(self->request_param, src); else g_string_assign(self->request_param, ""); /*LOG This message reports that a valid request is fetched from the client. */ z_proxy_log(self, FTP_REQUEST, 6, "Request fetched; req='%s' req_prm='%s'", self->request_cmd->str, self->request_param->str); g_string_up(self->request_cmd); self->command_desc = ftp_command_hash_get(self->request_cmd->str); if (!self->request_cmd->len && !self->permit_empty_command) { /*LOG This message indicates that an empty command is received, and policy does not permit it. */ z_proxy_log(self, FTP_VIOLATION, 1, "Empty command. Aborting;"); z_proxy_return(self, FALSE); } if (!self->command_desc && !self->permit_unknown_command && !ftp_policy_command_hash_search(self, self->request_cmd->str)) { /*LOG This message indicates that an unknown command is received and policy does not permit it. */ z_proxy_log(self, FTP_VIOLATION, 1, "Unknown command. Aborting; req='%s'", self->request_cmd->str); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } /** * ftp_command_process: * * This function processes the parsed command, and decides what to do. It * then either forwards or drops the command. */ void ftp_command_process(FtpProxy *self) { FtpInternalCommand *command = self->command_desc; int res; z_proxy_enter(self); SET_ANSWER(MSG_ERROR_PARSING_COMMAND); res = ftp_policy_command_hash_do(self); if (res == FTP_REQ_ACCEPT) { if (command) { if (!command->parse) { /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, known command but command parse is unset; req='%s'", self->request_cmd->str); assert(0); } res = command->parse(self); } } if (res == FTP_REQ_ACCEPT && self->state == FTP_NT_CLIENT_TO_PROXY) { /*LOG This message indicates that the given request was not permitted in non-transparent mode before the sever connection was established. It is likely caused by an AUTH command. */ z_proxy_log(self, FTP_ERROR, 3, "This command not allowed in non-transparent mode; req='%s'", self->request_cmd->str); res = FTP_REQ_REJECT; SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); } switch (res) { case FTP_REQ_ACCEPT: if (command && command->need_data) ftp_data_start(self); ftp_command_write_setup(self, self->request_cmd->str, self->request_param->str); break; case FTP_REQ_REJECT: ftp_command_reject(self); if (self->state == FTP_SERVER_TO_CLIENT) { ftp_state_set(self, EP_CLIENT); self->state = FTP_CLIENT_TO_SERVER; } else if (self->state == FTP_NT_SERVER_TO_PROXY) { ftp_state_set(self, EP_CLIENT); self->state = FTP_NT_CLIENT_TO_PROXY; } /*LOG This message indicates that the given request was rejected by the policy. */ z_proxy_log(self, FTP_POLICY, 3, "Request rejected; req='%s'", self->request_cmd->str); break; case FTP_PROXY_ANS: ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); if (self->state == FTP_SERVER_TO_CLIENT) { ftp_state_set(self, EP_CLIENT); self->state = FTP_CLIENT_TO_SERVER; } else if (self->state == FTP_NT_SERVER_TO_PROXY) { ftp_state_set( self, EP_CLIENT); self->state = FTP_NT_CLIENT_TO_PROXY; } /*LOG This message reports that the given request is answered by Zorp without sending the request to the server. */ z_proxy_log(self, FTP_POLICY, 4, "Proxy answer; rsp='%s' rsp_prm='%s'", self->answer_cmd->str, self->answer_param->str); break; case FTP_REQ_ABORT: SET_ANSWER(MSG_CONNECTION_ABORTED); ftp_command_reject(self); /*LOG This message indicates that the given request is rejected and the connection is aborted by Zorp. */ z_proxy_log(self, FTP_POLICY, 2, "Rejected command (aborting); line='%s'", self->line); self->state = FTP_QUIT; break; case FTP_NOOP: break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_POLICY, 1, "Bad policy type, aborting; line='%s', policy='%d'", self->line, res); self->state = FTP_QUIT; } z_proxy_return(self); } void ftp_command_reject(FtpProxy *self) { z_proxy_enter(self); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); z_proxy_return(self); } gboolean ftp_command_write(FtpProxy *self, char *msg) { gint bytes_to_write = strlen(msg); gboolean back; z_proxy_enter(self); back = ftp_stream_write(self, 'S', (const guchar *) msg, bytes_to_write); z_proxy_return(self, back); } gboolean ftp_command_write_setup(FtpProxy *self, gchar *answer_c, gchar *answer_p) { gchar newline[self->max_line_length]; gboolean vissza = TRUE; z_proxy_enter(self); if (strlen(answer_p) > 0) g_snprintf(newline, sizeof(newline), "%s %s", answer_c, answer_p); else g_snprintf(newline, sizeof(newline), "%s", answer_c); vissza = ftp_command_write(self,newline); z_proxy_return(self, vissza); } void ftp_proto_nt_init(FtpProxy *self) { z_proxy_enter(self); ftp_proto_state_set(self, FTP_STATE_PRECONNECT); if (self->auth) SET_ANSWER(MSG_NON_TRANSPARENT_GREETING_WITH_INBAND_AUTH) else SET_ANSWER(MSG_NON_TRANSPARENT_GREETING); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); self->state = FTP_NT_CLIENT_TO_PROXY; z_proxy_return(self); } void ftp_proto_nt_client_to_proxy(FtpProxy *self) { z_proxy_enter(self); if (!ftp_command_fetch(self) || !ftp_command_parse(self)) { self->state = FTP_QUIT; z_proxy_return(self); } if (!self->request_cmd->len) z_proxy_return(self); ftp_command_process(self); switch (self->ftp_state) { case FTP_STATE_PRECONNECT_LOGIN_P: /* by this time login creditentials, and host name is received, try to connect to the remote server */ if (self->auth && !self->auth_done) { z_proxy_log(self, FTP_ERROR, 3, "Inband authentication is required but wasn't completed;"); self->state = FTP_QUIT; break; } if (ftp_connect_server_event(self, self->hostname->str, self->hostport) && ftp_stream_server_init(self)) { self->state = FTP_NT_SERVER_TO_PROXY; ftp_proto_state_set(self, FTP_STATE_PRECONNECT); g_string_assign(self->request_cmd, ""); } else { self->state = FTP_QUIT; } break; case FTP_STATE_PRECONNECT_QUIT: self->state = FTP_QUIT; break; default: /* nothing to do */ break; } z_proxy_return(self); } static void ftp_proto_nt_send_USER(FtpProxy *self) { gchar user_line[self->username->len + 6]; z_proxy_enter(self); g_snprintf(user_line, sizeof(user_line), "USER %s", self->username->str); g_string_assign(self->request_cmd, "USER"); ftp_command_write(self, user_line); ftp_proto_state_set(self, FTP_STATE_PRECONNECT_LOGIN_U); z_proxy_leave(self); } static gboolean ftp_nt_check_cert_subject(FtpProxy *self) { ZProxyHostIface *host_iface; gboolean res = TRUE; z_proxy_enter(self); host_iface = Z_CAST(z_proxy_find_iface(&self->super, Z_CLASS(ZProxyHostIface)), ZProxyHostIface); if (host_iface) { gchar error_reason[256]; if (!z_proxy_host_iface_check_name(host_iface, self->hostname->str, error_reason, sizeof(error_reason))) { z_proxy_log(self, FTP_ERROR, 3, "Error checking hostname; error='%s'", error_reason); res = FALSE; } z_object_unref(&host_iface->super); } z_proxy_return(self, res); } void ftp_proto_nt_server_to_proxy(FtpProxy *self) { guint line_numbers = 0; z_proxy_enter(self); g_string_assign(self->answer_cmd, ""); self->answer_code = 0; self->answer_cont = 0; do { if (!ftp_answer_fetch(self, &self->answer_cont)) { self->state = FTP_QUIT; z_proxy_return(self); } line_numbers++; } while (self->answer_cont && line_numbers <= self->max_continuous_line); if (line_numbers > self->max_continuous_line) { /*LOG * This message reports that the server send an answer * which have too many lines. Increase the self.max_continuous_line variable * if this is not a security incident. */ z_proxy_log(self, FTP_POLICY, 3, "Too many continuous lines in the answer; max_continuous_line='%d'", self->max_continuous_line); self->state = FTP_QUIT; z_proxy_return(self); } switch (self->ftp_state) { case FTP_STATE_PRECONNECT: if (strcmp(self->answer_cmd->str, "220") == 0) { if (self->auth_tls_ok[EP_CLIENT] && (self->super.ssl_opts.security[EP_SERVER] == PROXY_SSL_SEC_FORWARD_STARTTLS)) { /* send AUTH TLS */ g_string_assign(self->request_cmd, "AUTH"); ftp_command_write(self, "AUTH TLS"); ftp_proto_state_set(self, FTP_STATE_PRECONNECT_AUTH); } else ftp_proto_nt_send_USER(self); } break; case FTP_STATE_PRECONNECT_AUTH: if (strcmp(self->answer_cmd->str, "234") == 0) { gboolean res; /* AUTH TLS accepted by server, do handshake */ res = z_proxy_ssl_request_handshake(&self->super, EP_SERVER, TRUE); if (!res) { z_proxy_log(self, FTP_ERROR, 2, "Server-side SSL handshake failed, terminating session;"); self->auth_tls_ok[EP_SERVER] = FALSE; SET_ANSWER(MSG_NT_SERVER_HANDSHAKE_FAILED); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); self->state = FTP_QUIT; break; } else self->auth_tls_ok[EP_SERVER] = TRUE; if (!ftp_nt_check_cert_subject(self)) { z_proxy_log(self, FTP_ERROR, 2, "Server-side SSL certificate subject does not match inband hostname, terminating session;"); SET_ANSWER(MSG_NT_SERVER_CERT_INVALID_SUBJECT); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); self->state = FTP_QUIT; break; } if (self->client_sent_pbsz) { g_string_assign(self->request_cmd, "PBSZ"); ftp_command_write(self, "PBSZ 0"); ftp_proto_state_set(self, FTP_STATE_PRECONNECT_PBSZ); } else ftp_proto_nt_send_USER(self); } else { z_proxy_log(self, FTP_ERROR, 3, "Server did not accept AUTH TLS in non-transparent mode, aborting;"); SET_ANSWER(MSG_NT_SERVER_AUTH_REJECT); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); self->state = FTP_QUIT; z_proxy_return(self); } break; case FTP_STATE_PRECONNECT_PBSZ: if (strcmp(self->answer_cmd->str, "200") == 0) { /* server accepted PBSZ 0: if PROT wasn't C, send prot; * otherwise send USER */ if (self->data_protection_enabled[EP_CLIENT]) { g_string_assign(self->request_cmd, "PROT"); ftp_command_write(self, "PROT P"); ftp_proto_state_set(self, FTP_STATE_PRECONNECT_PROT); } else ftp_proto_nt_send_USER(self); } else { z_proxy_log(self, FTP_ERROR, 3, "Server did not accept PBSZ in non-transparent mode, aborting;"); SET_ANSWER(MSG_NT_SERVER_PBSZV_REJECT); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); self->state = FTP_QUIT; z_proxy_return(self); } break; case FTP_STATE_PRECONNECT_PROT: if (strcmp(self->answer_cmd->str, "200") == 0) ftp_proto_nt_send_USER(self); else { z_proxy_log(self, FTP_ERROR, 3, "Server did not accept PROT in non-transparent mode, aborting;"); SET_ANSWER(MSG_NT_SERVER_PROT_REJECT); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); self->state = FTP_QUIT; z_proxy_return(self); } break; case FTP_STATE_PRECONNECT_LOGIN_U: if (strcmp(self->answer_cmd->str, "331") == 0) { gchar pass_line[self->password->len + 6]; /* send password */ g_snprintf(pass_line, sizeof(pass_line), "PASS %s", self->password->str); g_string_assign(self->request_cmd, "PASS"); ftp_command_write(self, pass_line); ftp_proto_state_set(self, FTP_STATE_LOGIN_P); self->state = FTP_SERVER_TO_CLIENT; ftp_state_set(self, EP_SERVER); } else if (strcmp(self->answer_cmd->str, "230") == 0) { /* no password required */ ftp_answer_write(self, self->line); ftp_proto_state_set(self, FTP_STATE_CONVERSATION); ftp_state_set(self, EP_CLIENT); self->state = FTP_CLIENT_TO_SERVER; } break; default: /* invalid state */ z_proxy_log(self, FTP_ERROR, 1, "Internal error while in non-transparent mode, proxy is in invalid state; state='%s'", ftp_proto_state_name(self->ftp_state)); self->state = FTP_QUIT; break; } z_proxy_return(self); } void ftp_proto_client_to_server(FtpProxy *self) { z_proxy_enter(self); if (!ftp_command_fetch(self) || !ftp_command_parse(self)) { self->state = FTP_QUIT; z_proxy_return(self); } if (!self->request_cmd->len) z_proxy_return(self); self->state = FTP_SERVER_TO_CLIENT; ftp_state_set(self, EP_SERVER); ftp_command_process(self); z_proxy_return(self); } void ftp_proto_server_to_client(FtpProxy *self) { guint line_numbers = 0; z_proxy_enter(self); g_string_assign(self->answer_cmd, ""); self->answer_code = 0; self->answer_cont = 0; do { if (!ftp_answer_fetch(self, &self->answer_cont)) { self->state = FTP_QUIT; z_proxy_return(self); } line_numbers++; } while (self->answer_cont && line_numbers <= self->max_continuous_line); if (line_numbers > self->max_continuous_line) { self->state = FTP_QUIT; z_proxy_return(self); } self->state = FTP_CLIENT_TO_SERVER; ftp_state_set(self, EP_CLIENT); ftp_answer_process(self); z_proxy_return(self); } static gboolean ftp_server_data(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { FtpProxy *self = (FtpProxy *) user_data; z_proxy_enter(self); ftp_proto_server_to_client(self); if (self->state == FTP_QUIT && self->transfer) { z_transfer2_cancel(self->transfer); self->transfer = NULL; } z_proxy_return(self, TRUE); } static gboolean ftp_client_data(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { FtpProxy *self = (FtpProxy *) user_data; z_proxy_enter(self); ftp_proto_client_to_server(self); if (self->state == FTP_QUIT && self->transfer) { z_transfer2_cancel(self->transfer); self->transfer = NULL; } z_proxy_return(self, TRUE); } void ftp_listen_both_side(FtpProxy *self) { guint rc; z_proxy_enter(self); if (!(self->data_state & FTP_DATA_CONVERSATION)) { rc = z_poll_iter_timeout(self->poll, self->timeout); if (!rc) { if (errno == ETIMEDOUT) { SET_ANSWER(MSG_TIMED_OUT); ftp_command_reject(self); } self->state = FTP_QUIT; } } else { rc = z_poll_iter_timeout(self->poll, -1); } if (self->data_state && self->state != FTP_QUIT) { if (rc) ftp_data_next_step(self); if (self->data_state && self->state != FTP_QUIT) self->state = FTP_BOTH_SIDE; } z_proxy_return(self); } gboolean ftp_config(ZProxy *s) { FtpProxy *self = Z_CAST(s, FtpProxy); ftp_config_set_defaults(self); ftp_proxy_regvars(self); if (Z_SUPER(s, ZProxy)->config(s)) { return ftp_config_init(self); } return FALSE; } void ftp_main(ZProxy *s) { FtpProxy *self = Z_CAST(s, FtpProxy); z_proxy_enter(self); if (!ftp_stream_client_init(self)) z_proxy_return(self); if (self->transparent_mode) self->state = FTP_INIT_TRANSPARENT; else self->state = FTP_INIT_NONTRANSPARENT; while (self->state != FTP_QUIT) { if (!z_proxy_loop_iteration(s)) { self->state = FTP_QUIT; break; } switch (self->state) { case FTP_INIT_TRANSPARENT: if (!ftp_connect_server_event(self, NULL, 0) || !ftp_stream_server_init(self)) { self->state = FTP_QUIT; } else { self->state = FTP_SERVER_TO_CLIENT; ftp_state_set(self, EP_SERVER); ftp_proto_state_set(self, FTP_STATE_LOGIN); } break; case FTP_INIT_NONTRANSPARENT: ftp_proto_nt_init(self); break; case FTP_NT_CLIENT_TO_PROXY: ftp_proto_nt_client_to_proxy(self); break; case FTP_NT_SERVER_TO_PROXY: ftp_proto_nt_server_to_proxy(self); break; case FTP_SERVER_TO_CLIENT: case FTP_CLIENT_TO_SERVER: case FTP_BOTH_SIDE: /*LOG This message reports that Zorp is reading from it's peers on the given side. */ z_proxy_log(self, FTP_DEBUG, 8, "Reading from peer; side='%s'", self->state == FTP_SERVER_TO_CLIENT ? "server" : self->state == FTP_CLIENT_TO_SERVER ? "client" : self->state == FTP_BOTH_SIDE ? "both" : "unknown"); ftp_listen_both_side(self); break; } } ftp_data_reset(self); ftp_deinit_streams(self); z_proxy_return(self); } ZProxy * ftp_proxy_new(ZProxyParams *params) { FtpProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(FtpProxy), params), FtpProxy); z_return(&self->super); } void ftp_proxy_free(ZObject *s) { guint i; FtpProxy *self = Z_CAST(s, FtpProxy); z_enter(); z_poll_quit(self->poll); z_poll_unref(self->poll); g_free(self->line); g_mutex_free(self->lock); if (self->preamble) g_free(self->preamble); for (i = 0; i < EP_MAX; i++) { z_sockaddr_unref(self->data_local_buf[i]); self->data_local_buf[i] = NULL; } z_proxy_free_method(s); z_return(); } ZProxyFuncs ftp_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), ftp_proxy_free, }, .config = ftp_config, .main = ftp_main, NULL }; Z_CLASS_DEF(FtpProxy, ZProxy, ftp_proxy_funcs); /*+ Zorp initialization function +*/ gint zorp_module_init (void) { ftp_command_hash_create(); z_registry_add ("ftp", ZR_PROXY, ftp_proxy_new); return TRUE; } zorp-3.9.5/modules/ftp/ftp.h000066400000000000000000000260131172670260400157340ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_FTP_H_INCLUDED #define ZORP_MODULES_FTP_H_INCLUDED #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftphash.h" #include "ftpolicy.h" #include "ftpmsg.h" #define FTP_LINE_MAX_LEN 2048 #define FTP_DEBUG "ftp.debug" #define FTP_ERROR "ftp.error" #define FTP_REQUEST "ftp.request" #define FTP_RESPONSE "ftp.response" #define FTP_POLICY "ftp.policy" #define FTP_VIOLATION "ftp.violation" #define FTP_SESSION "ftp.session" #define FTP_INFO "ftp.info" /* telnet processor states */ #define FTP_TELNET (0) #define FTP_TELNET_IAC (1) #define FTP_TELNET_IAC_DW (2) #define FTP_TELNET_IAC_FUNC (3) /* parser function return values */ #define FTP_REQ_ACCEPT 1 /* Allow command */ #define FTP_REQ_REJECT 3 /* Does'n allow, connection will remain */ #define FTP_REQ_ABORT 4 /* Doesn't allow, connection will dropped */ #define FTP_REQ_POLICY 6 /* Put the command up to policy */ #define FTP_NOOP 101 /* Do nothing, but not alert. */ #define FTP_PROXY_ANS 102 /* In non-transparent mode, proxy is answering */ #define FTP_RSP_ACCEPT 1 /* Allow answer */ #define FTP_RSP_REJECT 3 /* Does'n allow, connection will remain */ #define FTP_RSP_ABORT 4 /* Doesn't allow, connection will dropped */ #define FTP_RSP_POLICY 6 /* Put the answer up to policy */ #define FTP_STK_NONE 1 #define FTP_STK_DATA 2 enum ftp_feature_enum { FTP_FEATURE_ACCEPT = 1, FTP_FEATURE_DROP = 2, FTP_FEATURE_INSERT = 3, }; // // FTP statemachine states // enum ftp_state_enum { FTP_STATE_CONNECT = 0, FTP_STATE_LOGIN, FTP_STATE_LOGIN_U, FTP_STATE_LOGIN_P, FTP_STATE_LOGIN_A, FTP_STATE_PRECONNECT, FTP_STATE_PRECONNECT_FEAT, FTP_STATE_PRECONNECT_AUTH, FTP_STATE_PRECONNECT_PBSZ, FTP_STATE_PRECONNECT_PROT, FTP_STATE_PRECONNECT_LOGIN_U, FTP_STATE_PRECONNECT_LOGIN_P, FTP_STATE_PRECONNECT_QUIT, FTP_STATE_LOGINAUTH, FTP_STATE_CONVERSATION, FTP_STATE_RENAME, FTP_STATE_DATA, FTP_STATE_QUIT, FTP_STATE_MAX }; static gchar *ftp_state_names[] = { "CONNECT", "LOGIN", "LOGIN_U", "LOGIN_P", "LOGIN_A", "PRECONNECT", "PRECONNECT_FEAT", "PRECONNECT_AUTH", "PRECONNECT_PBSZ", "PRECONNECT_PROT", "PRECONNECT_LOGIN_U", "PRECONNECT_LOGIN_P", "PRECONNECT_QUIT", "LOGINAUTH", "CONVERSATION", "RENAME", "DATA", "QUIT", }; #define FTP_INIT_TRANSPARENT 0 #define FTP_INIT_NONTRANSPARENT 1 #define FTP_SERVER_TO_CLIENT 2 #define FTP_CLIENT_TO_SERVER 3 #define FTP_BOTH_SIDE 4 #define FTP_NT_CLIENT_TO_PROXY 5 #define FTP_NT_SERVER_TO_PROXY 6 #define FTP_QUIT 7 #define FTP_DATA_COMMAND_START (0x001) #define FTP_DATA_SERVER_START (0x002) #define FTP_DATA_SERVER_READY (0x004) #define FTP_DATA_SERVER_SAID (0x008) #define FTP_DATA_CLIENT_START (0x010) #define FTP_DATA_CLIENT_READY (0x020) #define FTP_DATA_CONVERSATION (0x040) #define FTP_DATA_CANCEL (0x080) #define FTP_DATA_DESTROY (0x100) #define FTP_SERVER_FIRST_READY (FTP_DATA_COMMAND_START | FTP_DATA_SERVER_START | FTP_DATA_SERVER_READY | FTP_DATA_SERVER_SAID) #define FTP_SERVER_CONNECT_READY (FTP_SERVER_FIRST_READY | FTP_DATA_CLIENT_START | FTP_DATA_CLIENT_READY) #define FTP_DATA_KEEP 0 /* keep connection mode */ #define FTP_DATA_PASSIVE 1 /* convert to passive mode */ #define FTP_DATA_ACTIVE 2 /* convert to active mode */ #define FTP_ACTIVE_MINUSONE 0 /* In active mode connect from command chanel minus one */ #define FTP_ACTIVE_TWENTY 1 /* In active mode connect from port 20 */ #define FTP_ACTIVE_RANDOM 3 /* In active mode connect from random port */ /* Command groups */ typedef struct _FtpProxy { ZProxy super; int state; /* I/O state in the proxy */ int oldstate; /* Where to go out from both side listening */ enum ftp_state_enum ftp_state; /* our state in the FTP protocol */ unsigned long data_state; /* data connection state */ ZPoll *poll; /* local permitted command & answer tables */ GHashTable *policy_command_hash; ZDimHashTable *policy_answer_hash; /* feature policy hash */ GHashTable *policy_features; /* command and answer buffer */ gchar *line; /* unparsed command */ gsize line_length; /* length of line */ guint max_line_length; /* command part */ GString *request_cmd; GString *request_param; FtpInternalCommand *command_desc; /* answer parts */ guint answer_code; guint answer_handle; GString *answer_cmd; GString *answer_param; gboolean answer_cont; /* protocol state variables */ GString *username; guint max_username_length; GString *password; guint max_password_length; GString *hostname; /* the name of the host to connect to in non-transparent mode */ guint hostport; guint max_hostname_length; GString *proxy_username; /**< username provided inband for authentication */ GString *proxy_password; /**< password provided inband for authentication */ guint proxy_auth_needed; /**< set (1) by parseInbandAuth; cleared (0) by C code after acting on it */ ZAuthProvider *auth; /**< inband authentication provider */ gboolean auth_done; /**< set to TRUE to indicate that the user has authenticated (inband) already */ GString *masq_address[EP_MAX]; guint active_connection_mode; ZSockAddr *data_local_buf[EP_MAX]; ZSockAddr *data_remote[EP_MAX]; ZSockAddr *data_local[EP_MAX]; guint server_port; ZDispatchEntry *data_listen[EP_MAX]; ZAttach *data_connect[EP_MAX]; ZStream *data_stream[EP_MAX]; ZStackedProxy *stacked_proxy; guint data_port_min; guint data_port_max; gboolean auth_tls_ok[EP_MAX]; gboolean data_protection_enabled[EP_MAX]; gboolean client_sent_pbsz; /* config variables accessible from Python */ gboolean transparent_mode; gint data_mode; gboolean permit_empty_command; gboolean permit_unknown_command; gboolean response_strip_msg; GString *valid_chars_username; ZCharSet username_charset; /* Timeout in both client and server side */ guint timeout; GString *target_port_range; guint max_continuous_line; /* Data connection protect */ GMutex *lock; gboolean ftp_data_hangup; ZTransfer2 *transfer; gsize buffer_size; gboolean drop_answer; gchar *preamble; } FtpProxy; extern ZClass FtpProxy__class; #define MSG_USERNAME_FORMAT_INVALID 0 #define MSG_HOSTNAME_TOO_LONG 1 #define MSG_USER_ALREADY_LOGGED_IN 2 #define MSG_CONNECTION_ABORTED 3 #define MSG_NON_TRANSPARENT_GREETING 4 #define MSG_LINE_TOO_LONG 5 #define MSG_LINE_TERM_CRLF 6 #define MSG_USERNAME_TOO_LONG 7 #define MSG_USER_FIRST 8 #define MSG_PASSWORD_TOO_LONG 9 #define MSG_USER_OKAY 10 #define MSG_COMMAND_NOT_ALLOWED_HERE 11 #define MSG_INVALID_PARAMETER 12 #define MSG_GOODBYE 13 #define MSG_MISSING_PARAMETER 14 #define MSG_COMMAND_NOT_IMPLEMENTED_P 15 #define MSG_COMMAND_NOT_RECOGNIZED 16 #define MSG_ANSWER_ERROR 17 #define MSG_ERROR_PARSING_PORT 18 #define MSG_ERROR_PARAMETER_PASV 19 #define MSG_ERROR_PARSING_PASV 20 #define MSG_ERROR_PARAMETER_PORT 21 #define MSG_PORT_SUCCESFULL 22 #define MSG_RNFR_RNTO 23 #define MSG_ERROR_PARSING_COMMAND 24 #define MSG_TIMED_OUT 25 #define MSG_ERROR_PARAMETER_EPSV 26 #define MSG_ERROR_PARAMETER_EPRT 27 #define MSG_DATA_TRANSFER_FAILED 28 #define MSG_NON_TRANSPARENT_GREETING_WITH_INBAND_AUTH 29 #define MSG_PASSWORD_FORMAT_INVALID 30 #define MSG_AUTH_TLS_SUCCESSFUL 31 #define MSG_PBSZ_SUCCESSFUL 32 #define MSG_PBSZ_PARAMETER_INVALID 33 #define MSG_PROT_PARAMETER_INVALID 34 #define MSG_PROT_SUCCESSFUL 35 #define MSG_COMMAND_NOT_IMPLEMENTED 36 #define MSG_NT_SERVER_HANDSHAKE_FAILED 37 #define MSG_NT_SERVER_CERT_INVALID_SUBJECT 38 #define MSG_NT_SERVER_AUTH_REJECT 39 #define MSG_NT_SERVER_PBSZV_REJECT 40 #define MSG_NT_SERVER_PROT_REJECT 41 #define MSG_USER_INBAND_INFO_INVALID 42 extern Ftp_message ftp_know_messages[]; gboolean ftp_data_prepare(FtpProxy *self, gint side, gchar mode); void ftp_data_start(FtpProxy *self); void ftp_data_reset(FtpProxy *self); void ftp_state_both(FtpProxy *self); gboolean ftp_data_transfer(FtpProxy *self, ZStream *from_stream, ZStream *to_stream); gchar *ftp_answer_setup(FtpProxy *self, gchar *answer_c, gchar *answer_p); gboolean ftp_answer_write(FtpProxy *self, gchar *msg); gboolean ftp_answer_write_with_setup(FtpProxy *self, gchar *answer_c, gchar *answer_p); void ftp_state_set(FtpProxy *self, guint order); static inline gchar * ftp_proto_state_name(const enum ftp_state_enum state) { return ftp_state_names[state]; } static inline void ftp_proto_state_set(FtpProxy *self, const enum ftp_state_enum new_state) { g_assert(new_state < FTP_STATE_MAX); if (self->ftp_state != new_state) { z_proxy_log(self, FTP_DEBUG, 6, "Transitioning protocol state machine; old_state='%s', new_state='%s'", ftp_proto_state_name(self->ftp_state), ftp_proto_state_name(new_state)); self->ftp_state = new_state; } } #define SET_ANSWER(answer) {\ g_string_assign(self->answer_cmd, ftp_know_messages[answer].code);\ g_string_assign(self->answer_param, ftp_know_messages[answer].long_desc); } #endif zorp-3.9.5/modules/ftp/ftpcmd.c000066400000000000000000002474001172670260400164200ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include "ftp.h" #include "ftphash.h" #include "ftpcmd.h" #include #include /** * Parse numeric parameters from an FTP command. * * @param[in] src parameter string * @param[in] length length of src * @param[out] nums array of numbers to hold the parameters * * This function is depend on the behaviour that in FTP protocol * the numbers are send in bytes. So it check that the numbers is between * 0 and 255. * If length is 0, this will return FALSE. * * @returns TRUE if the parameter string could be parsed correctly and completely **/ gboolean ftp_parse_nums(gchar *src, gint length, unsigned char *nums) { int i = 0; gchar *newsrc; z_enter(); if (length == 0) z_return(FALSE); while (length > 0 && i < 6) { unsigned int tmp; errno = 0; tmp = strtoul(src, &newsrc, 10); /* tmp is unsigned, so cannot be less than zero */ if (tmp > 255 || errno == ERANGE) z_return(FALSE); nums[i] = tmp; if (i < 5 && *newsrc != ',') z_return(FALSE); length -= (newsrc - src + 1); src = newsrc + 1; i++; } z_return(length <= 0); } gboolean ftp_parse_search_nums(gchar *src, gint length, unsigned char *nums) { gchar *left, *right; z_enter(); left = strchr(src,'('); if (left) { right = strrchr(src,')'); if (right) { left++; length = right - left; if (length > 0) z_return((ftp_parse_nums(left, length, nums))); } } z_return(FALSE); } /** * If the Python code told us to and we're actually using inband authentication, try to authenticate * using the provided proxy_username and proxy_password. * * @param[in] self FtpProxy instance. * * @returns FALSE if authentication was attempted and it failed **/ static gboolean ftp_do_inband_auth(FtpProxy *self) { if (!self->proxy_auth_needed) return TRUE; /* only attempt to authenticate if we're using inband authentication at all */ if (self->auth) { gboolean res; gchar **groups = NULL; z_policy_lock(self->super.thread); res = z_auth_provider_check_passwd(self->auth, self->super.session_id, self->proxy_username->str, self->proxy_password->str, &groups, &self->super); z_policy_unlock(self->super.thread); if (res) res = z_proxy_user_authenticated_default(&self->super, self->proxy_username->str, (gchar const **) groups); g_strfreev(groups); if (!res) { self->proxy_auth_needed = 0; return FALSE; /* authentication failed */ } } self->proxy_auth_needed = 0; /* reset the flag */ self->auth_done = TRUE; return TRUE; } guint ftp_command_parse_USER(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_PRECONNECT: case FTP_STATE_PRECONNECT_FEAT: case FTP_STATE_PRECONNECT_AUTH: case FTP_STATE_PRECONNECT_PBSZ: case FTP_STATE_PRECONNECT_PROT: case FTP_STATE_PRECONNECT_LOGIN_U: case FTP_STATE_PRECONNECT_LOGIN_P: /* USER in non transparent mode, we'll need to find out destination hostname */ /* parse auth information */ if (!ftp_policy_parse_authinfo(self, "USER", self->request_param)) { SET_ANSWER(MSG_USERNAME_FORMAT_INVALID); z_proxy_return(self, FTP_REQ_REJECT); } /* perform checks */ if (!z_port_enabled(self->target_port_range->str, self->hostport)) { SET_ANSWER(MSG_USERNAME_FORMAT_INVALID); /*LOG This message indicates that the port part of the username in non-transparent mode is not permitted by the policy and Zorp rejects the request. Check the 'target_port_range' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Invalid port specified in non-transparent destination; username='%s', " "port='%d', target_port_range='%s'", self->request_param->str, self->hostport, self->target_port_range->str); z_proxy_return(self, FTP_REQ_REJECT); } if (!z_charset_is_string_valid(&self->username_charset, self->username->str, self->username->len)) { /*LOG This message indicates that the username sent by the client contains invalid characters and Zorp rejects the request. Check the 'valid_chars_username' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Invalid character in username; username='%s', valid_chars_username='%s'", self->username->str, self->valid_chars_username->str); z_proxy_return(self, FTP_REQ_REJECT); } if (self->hostname->len > self->max_hostname_length) { SET_ANSWER(MSG_HOSTNAME_TOO_LONG); /*LOG This message indicates that the hostname part of the username in non-transparent mode is too long and Zorp rejects the request. Check the 'max_hostname_length' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Hostname specified in username is too long; username='%s', length='%zd', max_hostname_length='%d'", self->request_param->str, self->hostname->len, self->max_hostname_length); z_proxy_return(self, FTP_REQ_REJECT); } if (self->username->len > self->max_username_length) { SET_ANSWER(MSG_USERNAME_TOO_LONG); /*LOG This message indicates that the username is too long and Zorp rejects the request. Check the 'max_username_length' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Username too long; username='%s', length='%zd', max_username_length='%d'", self->request_param->str, self->username->len, self->max_username_length); z_proxy_return(self, FTP_REQ_REJECT); } /* do proxy authentication (inband) if necessary */ if (!ftp_do_inband_auth(self)) { SET_ANSWER(MSG_CONNECTION_ABORTED); z_proxy_log(self, FTP_ERROR, 3, "Authentication failed; proxy_username='%s'", self->proxy_username->str); z_proxy_return(self, FTP_REQ_ABORT); } if (self->proxy_username->len > 0 && self->proxy_password->len > 0 && self->username->len > 0 && self->password->len > 0) { /* the USER command supplied required data, no need to ask for password */ ftp_proto_state_set(self, FTP_STATE_PRECONNECT_LOGIN_P); z_proxy_return(self, FTP_NOOP); } else { ftp_proto_state_set(self, FTP_STATE_PRECONNECT_LOGIN_U); SET_ANSWER(MSG_USER_OKAY); z_proxy_trace(self, "USER Command ok;"); z_proxy_return(self, FTP_PROXY_ANS); } case FTP_STATE_LOGIN: case FTP_STATE_LOGIN_U: case FTP_STATE_LOGIN_P: case FTP_STATE_LOGIN_A: case FTP_STATE_LOGINAUTH: if (!self->transparent_mode) { /* The proxy is in non-transparent mode, but we received a second USER * request from the client: we have to be careful here, since the argument * might contain inband routing and authentication info. If that's the case, * we parse the argument and check that the destination server matches the * one we're connected to and re-do the inband authentication. */ self->proxy_auth_needed = FALSE; /* copy current inband routing data */ GString *old_hostname = g_string_new(self->hostname->str); if (ftp_policy_parse_authinfo(self, "USER", self->request_param)) { /* parsing succeeded, check that hostname is the same */ if (!g_string_equal(old_hostname, self->hostname)) { g_string_assign(self->hostname, old_hostname->str); SET_ANSWER(MSG_USER_INBAND_INFO_INVALID); z_proxy_log(self, FTP_POLICY, 3, "Re-sent username contains different inband " "routing information; old_hostname='%s', new_hostname='%s'", old_hostname->str, self->hostname->str); z_proxy_return(self, FTP_REQ_REJECT); } /* re-do inband authentication */ if (!ftp_do_inband_auth(self)) { SET_ANSWER(MSG_CONNECTION_ABORTED); z_proxy_log(self, FTP_ERROR, 3, "Authentication failed; proxy_username='%s'", self->proxy_username->str); z_proxy_return(self, FTP_REQ_ABORT); } /* send only the username part to the server */ g_string_assign(self->request_param, self->username->str); } g_string_assign(self->hostname, old_hostname->str); g_string_free(old_hostname, TRUE); } if (self->request_param->len > self->max_username_length) { SET_ANSWER(MSG_USERNAME_TOO_LONG); /*LOG This message indicates that the username is too long and Zorp rejects the request. Check the 'max_username_length' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Username too long; username='%s', length='%" G_GSIZE_FORMAT "', max_username_length='%d'", self->request_param->str, self->request_param->len, self->max_username_length); z_proxy_return(self, FTP_REQ_REJECT); } if (!z_charset_is_string_valid(&self->username_charset, self->request_param->str, self->request_param->len)) { /*LOG This message indicates that the username sent by the client contains invalid characters and Zorp rejects the request. Check the 'valid_chars_username' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Invalid character in username; username='%s', valid_chars_username='%s'", self->request_param->str, self->valid_chars_username->str); z_proxy_return(self, FTP_REQ_REJECT); } g_string_assign(self->username, self->request_param->str); ftp_proto_state_set(self, FTP_STATE_LOGIN_U); break; case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: SET_ANSWER(MSG_USER_ALREADY_LOGGED_IN); z_proxy_return(self, FTP_REQ_REJECT); default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='USER', state='%s'", ftp_proto_state_name(self->ftp_state)); z_proxy_return(self, FTP_REQ_ABORT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_USER(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_LOGIN_U: case FTP_STATE_LOGIN_P: switch(self->answer_cmd->str[0]) { case '2': ftp_proto_state_set(self, FTP_STATE_CONVERSATION); break; case '3': switch(self->answer_code) { case 331: break; case 332: ftp_proto_state_set(self, FTP_STATE_LOGIN_A); break; } break; } break; case FTP_STATE_CONVERSATION: /* do nothing, but accept response */ break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='USER', rsp='%u', state='%s'", self->answer_code, ftp_proto_state_name(self->ftp_state)); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_leave(self); return FTP_RSP_ACCEPT; } guint ftp_command_parse_PASS(FtpProxy *self) { guint clen; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_PRECONNECT: case FTP_STATE_PRECONNECT_LOGIN_P: /* PASS in non-transparent startup */ SET_ANSWER(MSG_USER_FIRST); z_proxy_return(self, FTP_REQ_REJECT); case FTP_STATE_PRECONNECT_LOGIN_U: if (self->request_param->len > self->max_password_length) { SET_ANSWER(MSG_PASSWORD_TOO_LONG); /*LOG This message indicates that the password is too long and Zorp rejects the request. Check the 'max_password_length' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Password too long; length='%" G_GSIZE_FORMAT "', max_password_length='%d'", self->request_param->len, self->max_password_length); z_proxy_return(self, FTP_REQ_REJECT); } /* parse auth information */ if (!ftp_policy_parse_authinfo(self, "PASS", self->request_param)) { SET_ANSWER(MSG_PASSWORD_FORMAT_INVALID); z_proxy_return(self, FTP_REQ_REJECT); } /* do proxy authentication (inband) if necessary */ if (!ftp_do_inband_auth(self)) { SET_ANSWER(MSG_CONNECTION_ABORTED); z_proxy_log(self, FTP_ERROR, 3, "Authentication failed; proxy_username='%s'", self->proxy_username->str); z_proxy_return(self, FTP_REQ_ABORT); } ftp_proto_state_set(self, FTP_STATE_PRECONNECT_LOGIN_P); z_proxy_return(self, FTP_NOOP); case FTP_STATE_LOGIN: case FTP_STATE_LOGIN_P: case FTP_STATE_LOGIN_A: case FTP_STATE_LOGINAUTH: SET_ANSWER(MSG_USER_FIRST); z_proxy_return(self, FTP_REQ_REJECT); case FTP_STATE_LOGIN_U: if (!self->transparent_mode) { /* The proxy is in non-transparent mode, but we received a second PASS * request from the client: we have to be careful here, since the argument * might contain inband authentication password. If that's the case, * we parse the argument, re-do the inband authentication and only pass * the password to the server. */ if (ftp_policy_parse_authinfo(self, "PASS", self->request_param)) { /* re-do inband authentication */ if (!ftp_do_inband_auth(self)) { SET_ANSWER(MSG_CONNECTION_ABORTED); z_proxy_log(self, FTP_ERROR, 3, "Authentication failed; proxy_username='%s'", self->proxy_username->str); z_proxy_return(self, FTP_REQ_ABORT); } /* send only the username part to the server */ g_string_assign(self->request_param, self->password->str); } } clen = strlen(self->request_param->str); if (clen > self->max_password_length) { SET_ANSWER(MSG_PASSWORD_TOO_LONG); /*LOG This message indicates that the password is too long and Zorp rejects the request. Check the 'max_password_length' attribute. */ z_proxy_log(self, FTP_POLICY, 3, "Password too long; length='%d', max_password_length='%d'", clen, self->max_password_length); z_proxy_return(self, FTP_REQ_REJECT); } g_string_assign(self->password, self->request_param->str); ftp_proto_state_set(self, FTP_STATE_LOGIN_P); break; case FTP_STATE_CONVERSATION: z_proxy_return(self, FTP_REQ_ACCEPT); case FTP_STATE_DATA: SET_ANSWER(MSG_USER_FIRST); z_proxy_return(self, FTP_REQ_REJECT); default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='PASS', state='%s'", ftp_proto_state_name(self->ftp_state)); z_proxy_return(self, FTP_REQ_ABORT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_PASS(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_LOGIN_P: switch (self->answer_cmd->str[0]) { case '2': ftp_proto_state_set(self, FTP_STATE_CONVERSATION); break; case '3': switch(self->answer_code) { case 332: ftp_proto_state_set(self, FTP_STATE_LOGIN_A); break; } break; } break; case FTP_STATE_CONVERSATION: /* do nothing, but accept response */ break; default: z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='PASS', rsp='%u', state='%s'", self->answer_code, ftp_proto_state_name(self->ftp_state)); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_ACCT(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_LOGIN: case FTP_STATE_LOGIN_U: case FTP_STATE_LOGIN_P: case FTP_STATE_LOGINAUTH: SET_ANSWER(MSG_USER_FIRST); z_proxy_return(self, FTP_REQ_REJECT); case FTP_STATE_LOGIN_A: case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='ACCT', state='%s'", ftp_proto_state_name(self->ftp_state)); z_proxy_return(self, FTP_REQ_ABORT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_ACCT(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_LOGIN_A: switch (self->answer_cmd->str[0]) { case '2': ftp_proto_state_set(self, FTP_STATE_CONVERSATION); break; } break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='ACCT', rsp='%u', state='%s'", self->answer_code, ftp_proto_state_name(self->ftp_state)); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_path(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: break; case FTP_STATE_DATA: if (self->command_desc->need_data) { ftp_state_both(self); self->state = FTP_BOTH_SIDE; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_path(FtpProxy *self) { z_proxy_enter(self); if (!self->command_desc->need_data) z_proxy_return(self, FTP_RSP_ACCEPT); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: switch (self->answer_cmd->str[0]) { case '1': self->oldstate = FTP_SERVER_TO_CLIENT; self->data_state |= FTP_DATA_SERVER_SAID; if (!self->command_desc || self->command_desc->need_data != 2) /* data: cli -> svr */ { self->preamble = ftp_answer_setup(self, self->answer_cmd->str, self->answer_param->str); self->drop_answer = TRUE; } else { self->preamble = NULL; } break; case '2': if (self->data_state != 0) self->oldstate = FTP_CLIENT_TO_SERVER; ftp_proto_state_set(self, FTP_STATE_CONVERSATION); if ((self->data_state & FTP_DATA_SERVER_SAID) == 0) /* if we've received no 150 */ ftp_data_reset(self); /* close any active data connection that might be left over; data_state is also reset */ break; case '4': case '5': if (self->data_state != 0) self->oldstate = FTP_CLIENT_TO_SERVER; ftp_proto_state_set(self, FTP_STATE_CONVERSATION); ftp_data_reset(self); break; default: /*LOG This message indicates that the data transfer command's answer sent by the server is invalid and Zorp resets the data transfer. */ z_proxy_log(self, FTP_VIOLATION, 1, "Unexpected response to data transfer command; req='%s', answer='%d'", self->request_cmd->str, self->answer_code); self->oldstate = FTP_CLIENT_TO_SERVER; ftp_data_reset(self); break; } break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='%s', rsp='%u', state='%s'", self->request_cmd->str, self->answer_code, ftp_proto_state_name(self->ftp_state)); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_QUIT(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_PRECONNECT: case FTP_STATE_PRECONNECT_FEAT: case FTP_STATE_PRECONNECT_AUTH: case FTP_STATE_PRECONNECT_PBSZ: case FTP_STATE_PRECONNECT_PROT: case FTP_STATE_PRECONNECT_LOGIN_U: case FTP_STATE_PRECONNECT_LOGIN_P: if (self->request_param->len > 0) { /*LOG This message indicates that the parameter of the request is invalid and Zorp rejects the request. This request must not have any parameter at all. */ z_proxy_log(self, FTP_VIOLATION, 3, "Invalid parameter for command; req='%s', req_param='%s'", self->request_cmd->str, self->request_param->str); SET_ANSWER(MSG_INVALID_PARAMETER); z_proxy_return(self, FTP_REQ_REJECT); } SET_ANSWER(MSG_GOODBYE); ftp_proto_state_set(self, FTP_STATE_PRECONNECT_QUIT); z_proxy_return(self, FTP_REQ_ABORT); case FTP_STATE_LOGIN: case FTP_STATE_LOGIN_U: case FTP_STATE_LOGIN_P: case FTP_STATE_LOGIN_A: case FTP_STATE_LOGINAUTH: case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: if (self->request_param->len > 0) { /*LOG This message indicates that the parameter of the request is invalid and Zorp rejects the request. This request must not have any parameter at all. */ z_proxy_log(self, FTP_VIOLATION, 3, "Invalid parameter for command; req='%s', req_param='%s'", self->request_cmd->str, self->request_param->str); SET_ANSWER(MSG_INVALID_PARAMETER); z_proxy_return(self, FTP_REQ_REJECT); } ftp_proto_state_set(self, FTP_STATE_QUIT); break; default: /*LOG This message indicates an internal error, please contact the BalaBit QA team. */ z_proxy_log(self, FTP_ERROR, 1, "Internal error, proxy in unknown state; cmd='QUIT', state='%s'", ftp_proto_state_name(self->ftp_state)); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_QUIT(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_QUIT: self->state = FTP_QUIT; break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_parse_TYPE(FtpProxy *self) { gchar mytype; z_proxy_enter(self); if(self->ftp_state == FTP_STATE_CONVERSATION || self->ftp_state == FTP_STATE_DATA) { if (self->request_param->len == 0) { SET_ANSWER(MSG_MISSING_PARAMETER); /*LOG This message indicates that the required parameter for the TYPE command is missing and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Missing parameter for the TYPE command;"); z_proxy_return(self, FTP_REQ_REJECT); } mytype = self->request_param->str[0]; switch(mytype) { case 'a': case 'A': case 'i': case 'I': g_string_truncate(self->request_param, 0); g_string_append_c(self->request_param, toupper(mytype)); g_string_up(self->request_param); break; case 'l': case 'L': case 'e': case 'E': /*LOG This message indicates that the requested transfer type specification is normally valid but currently unsupported by the proxy and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 3, "Valid, but unsupported transfer type specification; type='%c'", mytype); SET_ANSWER(MSG_COMMAND_NOT_IMPLEMENTED_P); z_proxy_return(self, FTP_REQ_REJECT); default: SET_ANSWER(MSG_COMMAND_NOT_RECOGNIZED); /*LOG This message indicates that the requested transfer type specification is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Unknown transfer type specification; type='%c'", mytype); z_proxy_return(self, FTP_REQ_REJECT); } } else { SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_parse_ABOR(FtpProxy *self) { char buf[3]; gsize len; z_proxy_enter(self); buf[0]=0xff; buf[1]=0xf4; buf[2]=0xff; if (self->ftp_state == FTP_STATE_CONVERSATION || self->ftp_state == FTP_STATE_DATA) { z_stream_write_pri(self->super.endpoints[EP_SERVER], buf, 3, &len, NULL); buf[0]=0xf2; z_stream_write(self->super.endpoints[EP_SERVER], buf, 1, &len, NULL); self->state = FTP_SERVER_TO_CLIENT; ftp_proto_state_set(self, FTP_STATE_CONVERSATION); z_proxy_return(self, FTP_REQ_ACCEPT); } else if (self->ftp_state == FTP_STATE_RENAME) { ftp_proto_state_set(self, FTP_STATE_CONVERSATION); } SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } guint ftp_command_answer_ABOR(FtpProxy *self) { z_proxy_enter(self); if (self->ftp_state == FTP_STATE_CONVERSATION || self->ftp_state == FTP_STATE_DATA) { if (self->answer_cmd->str[0] == '2') { ftp_proto_state_set(self, FTP_STATE_CONVERSATION); self->oldstate = FTP_CLIENT_TO_SERVER; } else if (self->answer_cmd->str[0] == '4') { self->oldstate = FTP_SERVER_TO_CLIENT; self->data_state = 0; } } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_noarg(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: g_string_assign(self->request_param, ""); break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_HELP(FtpProxy *self G_GNUC_UNUSED) { return FTP_RSP_ACCEPT; } guint ftp_command_parse_MODE(FtpProxy *self) { char mymode; z_proxy_enter(self); if (self->ftp_state == FTP_STATE_CONVERSATION || self->ftp_state == FTP_STATE_DATA) { if (self->request_param->len == 0) { SET_ANSWER(MSG_MISSING_PARAMETER); /*LOG This message indicates that the required parameter for the MODE command is missing and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Missing parameter to the MODE command;"); z_proxy_return(self, FTP_REQ_REJECT); } mymode = self->request_param->str[0]; switch(mymode) { case 's': case 'S': case 'b': case 'B': case 'c': case 'C': g_string_truncate(self->request_param, 0); g_string_append_c(self->request_param, toupper(mymode)); break; default: /*LOG This message indicates that the MODE command parameter is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Invalid parameter to the MODE command; mode='%c'", mymode); SET_ANSWER(MSG_COMMAND_NOT_RECOGNIZED); z_proxy_return(self, FTP_REQ_REJECT); } } else { SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_parse_string(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_parse_STRU(FtpProxy *self) { char mystru; z_proxy_enter(self); if (self->ftp_state == FTP_STATE_CONVERSATION || self->ftp_state == FTP_STATE_DATA) { if (self->request_param->len == 0) { SET_ANSWER(MSG_MISSING_PARAMETER); /*LOG This message indicates that the required parameter for the STRU command is missing and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Missing parameter to the STRU command;"); z_proxy_return(self, FTP_REQ_REJECT); } mystru = self->request_param->str[0]; switch(mystru) { case 'f': case 'F': g_string_truncate(self->request_param, 0); g_string_append_c(self->request_param, toupper(mystru)); break; default: SET_ANSWER(MSG_COMMAND_NOT_RECOGNIZED); /*LOG This message indicates that the STRU command parameter is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Invalid parameter to the STRU command; stru='%c'", mystru); z_proxy_return(self, FTP_REQ_REJECT); } } else { SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_data_server_start_PORT(FtpProxy *self) { guint port; gchar tmpaddr[16]; z_proxy_enter(self); if (!ftp_data_prepare(self, EP_SERVER, 'L')) { SET_ANSWER(MSG_ERROR_PARSING_PORT); self->data_state = 0; /*LOG This message indicates that Zorp was unable to start listening for the data connection on the server side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing server-side data connection (PORT);"); z_proxy_return(self, FTP_REQ_REJECT); } if (self->masq_address[EP_SERVER]->len) g_strlcpy(tmpaddr, self->masq_address[EP_SERVER]->str, sizeof(tmpaddr)); else z_inet_ntoa(tmpaddr, sizeof(tmpaddr), ((struct sockaddr_in *) &self->data_local[EP_SERVER]->sa)->sin_addr); g_strdelimit(tmpaddr, ".", ','); /* FIXME: This check must be not in here. maybe in z_dispatch_register? */ port = ntohs(((struct sockaddr_in *) &self->data_local[EP_SERVER]->sa)->sin_port); if (port == 0) { SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the server side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "There was an error binding a server-side listener;"); z_proxy_return(self, FTP_REQ_REJECT); } g_string_sprintf(self->request_param, "%s,%d,%d", tmpaddr, (port & 0xff00) >> 8, port & 0x00ff); z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_data_server_start_PASV(FtpProxy *self) { guchar nums[6]; gchar ip[17]; guint16 port; z_proxy_enter(self); if (!ftp_parse_search_nums(self->answer_param->str, self->answer_param->len, nums)) { SET_ANSWER(MSG_ERROR_PARAMETER_PASV); /*LOG This message indicates that the response to the PASV command is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Error parsing PASV response; param='%s'", self->answer_param->str); z_proxy_return(self, FTP_RSP_REJECT); } g_snprintf(ip, sizeof(ip), "%d.%d.%d.%d", nums[0], nums[1], nums[2], nums[3]); port = nums[4] * 256 + nums[5]; self->data_remote[EP_SERVER] = z_sockaddr_inet_new(ip, port); if (!ftp_data_prepare(self, EP_SERVER, 'C')) { SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that the proxy was unable to connect to the server on the port specified in its PASV response and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing data connection to the server (PASV);"); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_data_server_start_EPRT(FtpProxy *self) { guint port; gchar tmpaddr[16]; z_proxy_enter(self); if (!ftp_data_prepare(self, EP_SERVER, 'L')) { SET_ANSWER(MSG_ERROR_PARSING_PORT); self->data_state = 0; /*LOG This message indicates that Zorp was unable to start listening for the data connection on the server side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing server-side data connection listener (EPRT);"); z_proxy_return(self, FTP_REQ_REJECT); } if (self->masq_address[EP_SERVER]->len) g_strlcpy(tmpaddr, self->masq_address[EP_SERVER]->str, sizeof(tmpaddr)); else z_inet_ntoa(tmpaddr, sizeof(tmpaddr), ((struct sockaddr_in *) &self->data_local[EP_SERVER]->sa)->sin_addr); port = ntohs(((struct sockaddr_in *) &self->data_local[EP_SERVER]->sa)->sin_port); if (port == 0) { SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the server side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Cannot bind to the given address (EPRT);"); z_proxy_return(self, FTP_REQ_REJECT); } g_string_printf(self->request_param, "|1|%s|%d|", tmpaddr, port); z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_data_server_start_EPSV(FtpProxy *self) { gchar **split; guint port; gchar *err; gchar tmpline[FTP_LINE_MAX_LEN]; gchar *start, *end; gchar delim[2]; ZPolicyObj *sockaddr; ZSockAddr *tmpaddr; gchar tmpip[16]; guint res = FTP_RSP_ACCEPT; z_proxy_enter(self); if (self->answer_param->len <= 0) { /*LOG This message indicates that the required parameter for the EPSV command is missing and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Missing parameter (EPSV);"); z_proxy_return(self, FTP_RSP_REJECT); } g_strlcpy(tmpline, self->answer_param->str, sizeof(tmpline)); start = strchr(tmpline, '('); if (!start) { /*LOG This message indicates that the parameter of the EPSV response does not begin with a bracket and Zorp rejcets the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad parameter (EPSV), not beginning with bracket; rsp_param='%s'", self->answer_param->str); z_proxy_return(self, FTP_RSP_REJECT); } *start = 0; end = strchr(start + 1, ')'); if (!end) { /*LOG This message indicates that the parameter of the EPSV response does not have a closing bracket and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad parameter (EPSV), not closing with bracket; rsp_param='%s'", self->answer_param->str); z_proxy_return(self, FTP_RSP_REJECT); } *end = 0; delim[0] = start[1]; delim[1] = 0; split = g_strsplit(start + 1, delim, 6); if (split == NULL || split[0] == NULL || split[1] == NULL || split[2] == NULL || split[3] == NULL || split[4] == NULL || split[5] != NULL) { SET_ANSWER(MSG_ERROR_PARAMETER_EPSV); /*LOG This message indicates that the EPSV command response is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Error parsing EPSV response; param='%s'", self->answer_param->str); res = FTP_RSP_REJECT; goto exit; } if (strlen(split[1]) == 0 || strcmp(split[1],"1") == 0) { port = strtol(split[3], &err, 10); if (port == 0 || *err != 0) { SET_ANSWER(MSG_ERROR_PARAMETER_EPSV); g_strfreev(split); /*LOG This message indicates that the port number of the EPSV command response is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad parameter (EPSV), invalid port; rsp_param='%s'", self->answer_param->str); res = FTP_RSP_REJECT; goto exit; } if (strlen(split[2]) > 0) { self->data_remote[EP_SERVER] = z_sockaddr_inet_new(split[2], port); } else { /* FIXME: use z_proxy_get_addresses */ z_policy_lock(self->super.thread); sockaddr = z_session_getattr(self->super.handler, "server_address"); if (!sockaddr || !z_policy_sockaddr_check(sockaddr)) { z_policy_unlock(self->super.thread); /*LOG This message indicates that Proxy cannot detect the server address. This an internal error. */ z_proxy_log(self, FTP_VIOLATION, 2, "Internal error, cannot detect server address;"); res = FTP_RSP_REJECT; goto exit; } tmpaddr = z_policy_sockaddr_get_sa(sockaddr); z_inet_ntoa(tmpip, sizeof(tmpip), ((struct sockaddr_in *) &tmpaddr->sa)->sin_addr); z_sockaddr_unref(tmpaddr); self->data_remote[EP_SERVER] = z_sockaddr_inet_new(tmpip, port); z_policy_unlock(self->super.thread); } } else { SET_ANSWER(MSG_ERROR_PARAMETER_EPSV); g_strfreev(split); /*LOG This message indicates that the protocol specified by the EPSV command response is not supported by the proxy and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 1, "Unknown protocol type (EPSV); protocol='%s', rsp_param='%s'", split[1], self->answer_param->str); res = FTP_RSP_REJECT; goto exit; } if (!ftp_data_prepare(self, EP_SERVER, 'C')) { SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that the proxy was unable to connect to the server on the port specified in its EPSV response and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing data connection to the server (EPSV);"); res = FTP_RSP_REJECT; } exit: if (split) g_strfreev(split); z_proxy_return(self, res); } /** * Parse PORT FTP command. * * @param[in] self FtpProxy instance * * @returns FTP_REQ_ACCEPT if the connection is to be accepted, FTP_REQ_REJECT if it should be rejected **/ guint ftp_command_parse_PORT(FtpProxy *self) { guchar nums[6]; gchar ip[17]; guint16 port; guint res = FTP_REQ_ACCEPT; z_proxy_enter(self); if (self->ftp_state == FTP_STATE_DATA) { ftp_proto_state_set(self, FTP_STATE_CONVERSATION); ftp_data_reset(self); } switch (self->ftp_state) { case FTP_STATE_CONVERSATION: if (!ftp_parse_nums(self->request_param->str, self->request_param->len, nums)) { SET_ANSWER(MSG_ERROR_PARAMETER_PORT); /*LOG This message indicates that the parameter of the PORT command is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Invalid parameters to the PORT command; param='%s'", self->request_param->str); z_proxy_return(self, FTP_REQ_REJECT); } g_snprintf(ip, sizeof(ip), "%d.%d.%d.%d", nums[0], nums[1], nums[2], nums[3]); port = nums[4] * 256 + nums[5]; self->data_remote[EP_CLIENT] = z_sockaddr_inet_new(ip, port); switch (self->data_mode) { case FTP_DATA_PASSIVE: g_string_assign(self->request_cmd, "PASV"); g_string_assign(self->request_param, ""); break; case FTP_DATA_ACTIVE: case FTP_DATA_KEEP: res = ftp_data_server_start_PORT(self); break; default: /*LOG This message indicates that the 'data_mode' attribute of the policy is invalid and Zorp rejects the request. Check the 'data_mode' attribute. */ z_proxy_log(self, FTP_POLICY, 1, "Connection mode not supported; data_mode='%d'", self->data_mode); SET_ANSWER(MSG_ERROR_PARSING_PORT); z_proxy_return(self, FTP_REQ_REJECT); } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, res); } guint ftp_command_answer_PORT(FtpProxy *self) { guint res = FTP_RSP_ACCEPT; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: switch (self->data_mode) { case FTP_DATA_PASSIVE: switch (self->answer_cmd->str[0]) { case '2': res = ftp_data_server_start_PASV(self); if (res == FTP_RSP_ACCEPT) { if (!ftp_data_prepare(self, EP_CLIENT, 'C')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that the proxy was unable to connect to the client on the port specified in its PORT response and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client-side data connection (PORT->PASV);"); z_proxy_return(self, FTP_RSP_REJECT); } SET_ANSWER(MSG_PORT_SUCCESFULL); res = FTP_RSP_ACCEPT; } ftp_proto_state_set(self, FTP_STATE_DATA); break; case '4': case '5': ftp_data_reset(self); z_proxy_return(self, FTP_RSP_ACCEPT); default: SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that the response of the PASV command is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Error parsing the server answer to the PASV command (PORT->PASV); answer='%s'", self->answer_cmd->str); ftp_data_reset(self); z_proxy_return(self, FTP_RSP_REJECT); } break; case FTP_DATA_ACTIVE: case FTP_DATA_KEEP: switch (self->answer_cmd->str[0]) { case '2': if (!ftp_data_prepare(self, EP_CLIENT, 'C')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that the proxy was unable to connect to the client on the port specified in its PORT response and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client-side data connection (PORT);"); z_proxy_return(self, FTP_RSP_REJECT); } ftp_proto_state_set(self, FTP_STATE_DATA); break; case '4': case '5': ftp_data_reset(self); z_proxy_return(self, FTP_RSP_ACCEPT); default: SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that the response of the PORT command is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Error parsing the server answer to the PORT command; answer='%s'", self->answer_cmd->str); ftp_data_reset(self); z_proxy_return(self, FTP_RSP_ACCEPT); } break; default: break; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, res); } guint ftp_command_parse_PASV(FtpProxy *self) { guint res = FTP_REQ_ACCEPT; z_proxy_enter(self); if (self->ftp_state == FTP_STATE_DATA) { ftp_proto_state_set(self, FTP_STATE_CONVERSATION); ftp_data_reset(self); } switch (self->ftp_state) { case FTP_STATE_CONVERSATION: g_string_truncate(self->request_param, 0); self->data_state = 0; switch (self->data_mode) { case FTP_DATA_KEEP: case FTP_DATA_PASSIVE: break; case FTP_DATA_ACTIVE: g_string_assign(self->request_cmd, "PORT"); g_string_truncate(self->request_param, 0); res = ftp_data_server_start_PORT(self); break; default: /*LOG This message indicates that the 'data_mode' attribute of the policy is invalid and Zorp rejects the request. Check the 'data_mode' attribute. */ z_proxy_log(self, FTP_POLICY, 1, "Connection mode not supported; data_mode='%d'", self->data_mode); SET_ANSWER(MSG_ERROR_PARSING_PORT); z_proxy_return(self, FTP_REQ_REJECT); } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, res); } guint ftp_command_answer_PASV(FtpProxy *self) { gchar *start, *end; guint port; gchar tmpline[FTP_LINE_MAX_LEN]; gchar tmpaddr[16]; guint ret = FTP_RSP_ACCEPT; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: switch (self->data_mode) { case FTP_DATA_PASSIVE: case FTP_DATA_KEEP: switch (self->answer_cmd->str[0]) { case '2': ret = ftp_data_server_start_PASV(self); if (ret == FTP_RSP_ACCEPT) { if (!ftp_data_prepare(self, EP_CLIENT, 'L')) { ftp_data_reset(self); SET_ANSWER(MSG_ERROR_PARSING_PASV); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client-side data connection listener (PASV); error='bind error'"); z_proxy_return(self, FTP_RSP_REJECT); } if (self->masq_address[EP_CLIENT]->len) g_strlcpy(tmpaddr, self->masq_address[EP_CLIENT]->str, sizeof(tmpaddr)); else z_inet_ntoa(tmpaddr, sizeof(tmpaddr), ((struct sockaddr_in *) &self->data_local[EP_CLIENT]->sa)->sin_addr); g_strdelimit(tmpaddr, ".", ','); port = ntohs(((struct sockaddr_in *) &self->data_local[EP_CLIENT]->sa)->sin_port); if (port == 0) { ftp_data_reset(self); SET_ANSWER(MSG_ERROR_PARSING_PASV); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client-side data connection listener (PASV); error='port is invalid'"); z_proxy_return(self, FTP_RSP_REJECT); } g_strlcpy(tmpline, self->answer_param->str, sizeof(tmpline)); g_string_truncate(self->answer_param, 0); start = strchr(tmpline, '('); end = NULL; if (start) { *start = 0; end = strchr(start, ')'); g_string_assign(self->answer_param, tmpline); } g_string_append_printf(self->answer_param, "(%s,%d,%d)%s", tmpaddr, (port & 0xff00) >> 8, port & 0x00ff, end ? end + 1 : ""); } ftp_proto_state_set(self, FTP_STATE_DATA); break; default: self->data_state = 0; z_proxy_return(self, FTP_RSP_ACCEPT); } break; case FTP_DATA_ACTIVE: switch (self->answer_cmd->str[0]) { case '2': if (!ftp_data_prepare(self, EP_CLIENT, 'L')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PASV); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client-side data connection listener (PASV->PORT);"); z_proxy_return(self, FTP_RSP_REJECT); } g_string_assign(self->answer_cmd, "227"); if (self->masq_address[EP_CLIENT]->len) g_strlcpy(tmpaddr, self->masq_address[EP_CLIENT]->str, sizeof(tmpaddr)); else z_inet_ntoa(tmpaddr, sizeof(tmpaddr), ((struct sockaddr_in *) &self->data_local[EP_CLIENT]->sa)->sin_addr); g_strdelimit(tmpaddr, ".", ','); port = ntohs(((struct sockaddr_in *) &self->data_local[EP_CLIENT]->sa)->sin_port); if (port==0) { SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the request. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client-side data connection listener (PASV->PORT);"); z_proxy_return(self, FTP_RSP_REJECT); } g_string_sprintf(self->answer_param, "Entering Passive mode (%s,%d,%d).", tmpaddr, (port & 0xff00) >> 8, port & 0x00ff); ftp_proto_state_set(self, FTP_STATE_DATA); break; default: SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that the response of the PORT command is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Error parsing the server answer to the PORT command (PASV->PORT); answer='%s'", self->answer_cmd->str); z_proxy_return(self, FTP_RSP_REJECT); } break; default: break; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, ret); } guint ftp_command_parse_EPRT(FtpProxy *self) { guint16 port; guint res = FTP_REQ_ACCEPT; gchar **split; gchar delim[2]; gchar *err; z_proxy_enter(self); if (self->ftp_state == FTP_STATE_DATA) { ftp_proto_state_set(self, FTP_STATE_CONVERSATION); ftp_data_reset(self); } switch (self->ftp_state) { case FTP_STATE_CONVERSATION: if (self->request_param->len <= 0) { /*LOG This message indicates that the required parameter for the EPRT command is missing and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Missing parameter (EPRT);"); z_proxy_return(self, FTP_RSP_REJECT); } delim[0] = self->request_param->str[0]; delim[1] = 0; split = g_strsplit(self->request_param->str, delim, 6); if (split[0] == NULL || split[1] == NULL || split[2] == NULL || split[3] == NULL || split[4] == NULL || split[5] != NULL) { SET_ANSWER(MSG_ERROR_PARAMETER_EPRT); g_strfreev(split); /*LOG This message indicates that the parameter of the EPRT command is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad parameter (EPRT); req_param='%s'", self->request_param->str); z_proxy_return(self, FTP_REQ_REJECT); } if (strcmp(split[1],"1") == 0) { port = strtol(split[3], &err, 10); if (port == 0 || *err != 0) { SET_ANSWER(MSG_ERROR_PARAMETER_EPRT); g_strfreev(split); /*LOG This message indicates that the port number of the EPRT command is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad port parameter (EPRT); req_param='%s'", self->request_param->str); z_proxy_return(self, FTP_REQ_REJECT); } } else { SET_ANSWER(MSG_ERROR_PARAMETER_EPRT); g_strfreev(split); /*LOG This message indicates that the protocol specified by the EPRT command is not supported by the proxy and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Unknown protocol method (EPRT); protocol='%s', req_param='%s'", split[1], self->request_param->str); z_proxy_return(self, FTP_REQ_REJECT); } self->data_remote[EP_CLIENT] = z_sockaddr_inet_new(split[2], port); g_strfreev(split); if (!self->data_remote[EP_CLIENT]) { SET_ANSWER(MSG_ERROR_PARAMETER_EPRT); /*LOG This message indicates that the host address of the EPRT command is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad host address (EPRT); ip='%s', req_param='%s'", split[2], self->request_param->str); z_proxy_return(self, FTP_REQ_REJECT); } switch (self->data_mode) { case FTP_DATA_PASSIVE: g_string_assign(self->request_cmd, "EPSV"); g_string_assign(self->request_param, ""); break; case FTP_DATA_ACTIVE: case FTP_DATA_KEEP: res = ftp_data_server_start_EPRT(self); break; default: /*LOG This message indicates that the 'data_mode' attribute of the policy is invalid and Zorp rejects the request. Check the 'data_mode' attribute. */ z_proxy_log(self, FTP_POLICY, 1, "Connection mode not supported; data_mode='%d'", self->data_mode); SET_ANSWER(MSG_ERROR_PARSING_PORT); z_proxy_return(self, FTP_REQ_REJECT); } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, res); } guint ftp_command_answer_EPRT(FtpProxy *self) { guint res = FTP_RSP_ACCEPT; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: switch (self->data_mode) { case FTP_DATA_PASSIVE: switch (self->answer_cmd->str[0]) { case '2': res = ftp_data_server_start_EPSV(self); if (res == FTP_RSP_ACCEPT) { if (!ftp_data_prepare(self, EP_CLIENT, 'C')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that the proxy was unable to connect to the client specified in the EPRT response and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client connect (EPRT);"); z_proxy_return(self, FTP_RSP_REJECT); } SET_ANSWER(MSG_PORT_SUCCESFULL); res = FTP_RSP_ACCEPT; } ftp_proto_state_set(self, FTP_STATE_DATA); break; default: SET_ANSWER(MSG_ERROR_PARSING_PORT); self->data_state = 0; /*LOG This message indicates that the response of the EPRT is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad server answer (EPRT); rsp='%s'", self->answer_cmd->str); z_proxy_return(self, FTP_RSP_REJECT); } break; case FTP_DATA_ACTIVE: case FTP_DATA_KEEP: switch (self->answer_cmd->str[0]) { case '2': if (!ftp_data_prepare(self, EP_CLIENT, 'C')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PORT); /*LOG This message indicates that the proxy was unable to connect to the client specified in the EPRT response and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client connect (EPRT);"); z_proxy_return(self, FTP_RSP_REJECT); } ftp_proto_state_set(self, FTP_STATE_DATA); break; default: self->data_state = 0; z_proxy_return(self, FTP_RSP_ACCEPT); } break; default: break; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, res); } guint ftp_command_parse_EPSV(FtpProxy *self) { guint res = FTP_REQ_ACCEPT; z_proxy_enter(self); if (self->ftp_state == FTP_STATE_DATA) { ftp_proto_state_set(self, FTP_STATE_CONVERSATION); ftp_data_reset(self); } switch (self->ftp_state) { case FTP_STATE_CONVERSATION: g_string_assign(self->request_param, ""); self->data_state = 0; switch (self->data_mode) { case FTP_DATA_KEEP: case FTP_DATA_PASSIVE: break; case FTP_DATA_ACTIVE: g_string_assign(self->request_cmd, "EPRT"); g_string_assign(self->request_param, ""); res = ftp_data_server_start_EPRT(self); break; default: /*LOG This message indicates that the 'data_mode' attribute of the policy is invalid and Zorp rejects the request. Check the 'data_mode' attribute. */ z_proxy_log(self, FTP_POLICY, 1, "Connection mode not supported; data_mode='%d'", self->data_mode); SET_ANSWER(MSG_ERROR_PARSING_PORT); z_proxy_return(self, FTP_REQ_REJECT); } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, res); } guint ftp_command_answer_EPSV(FtpProxy *self) { gchar *start, *end; guint port; gchar tmpline[FTP_LINE_MAX_LEN]; guint ret = FTP_RSP_ACCEPT; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: switch (self->data_mode) { case FTP_DATA_PASSIVE: case FTP_DATA_KEEP: switch (self->answer_cmd->str[0]) { case '2': ret = ftp_data_server_start_EPSV(self); if (ret == FTP_RSP_ACCEPT) { if (!ftp_data_prepare(self, EP_CLIENT, 'L')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PASV); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client listen (EPSV);"); z_proxy_return(self, FTP_RSP_REJECT); } port = ntohs(((struct sockaddr_in *) &self->data_local[EP_CLIENT]->sa)->sin_port); if (port == 0) { SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client listen (EPSV);"); z_proxy_return(self, FTP_RSP_REJECT); } g_strlcpy(tmpline, self->answer_param->str, sizeof(tmpline)); start = strchr(tmpline, '('); end = NULL; if (start) { *start = 0; end = strchr(start + 1, ')'); g_string_assign(self->answer_param, tmpline); } g_string_append_printf(self->answer_param, "(|||%d|)", port); if (end) g_string_append(self->answer_param, end + 1); } ftp_proto_state_set(self, FTP_STATE_DATA); break; default: self->data_state = 0; z_proxy_return(self, FTP_RSP_ACCEPT); } break; case FTP_DATA_ACTIVE: switch (self->answer_cmd->str[0]) { case '2': if (!ftp_data_prepare(self, EP_CLIENT, 'L')) { self->data_state = 0; SET_ANSWER(MSG_ERROR_PARSING_PASV); /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client listen (EPSV);"); z_proxy_return(self, FTP_RSP_REJECT); } g_string_assign(self->answer_cmd, "229"); port = ntohs(((struct sockaddr_in *) &self->data_local[EP_CLIENT]->sa)->sin_port); if(port==0) { SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that Zorp was unable to start listening for the data connection on the client side and Zorp rejects the response. */ z_proxy_log(self, FTP_ERROR, 2, "Error preparing client listen (EPSV);"); z_proxy_return(self, FTP_RSP_REJECT); } g_string_printf(self->answer_param, "Entering Extended Passive Mode (|||%d|)", port); ftp_proto_state_set(self, FTP_STATE_DATA); break; default: SET_ANSWER(MSG_ERROR_PARSING_PASV); self->data_state = 0; /*LOG This message indicates that the response of the EPSV command is invalid and Zorp rejects the response. */ z_proxy_log(self, FTP_VIOLATION, 2, "Bad server answer (EPSV); rsp='%s'", self->answer_cmd->str); z_proxy_return(self, FTP_RSP_REJECT); } break; default: break; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, ret); } guint ftp_command_answer_RNFR(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: switch(self->answer_code) { case 350: ftp_proto_state_set(self, FTP_STATE_RENAME); break; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_RNTO(FtpProxy *self) { guint res = FTP_REQ_ACCEPT; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_RENAME: ftp_proto_state_set(self, FTP_STATE_CONVERSATION); res = ftp_command_parse_path(self); break; default: SET_ANSWER(MSG_RNFR_RNTO); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, res); } guint ftp_command_parse_ALLO(FtpProxy *self) { glong num1; glong num2; gchar *str; gchar *endptr; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: case FTP_STATE_DATA: if (self->request_param->len == 0) break; str = self->request_param->str; num1 = strtol(str, &endptr, 10); if (num1 < 0 || ((num1 == LONG_MAX || num1 == LONG_MIN) && errno == ERANGE)) { z_proxy_log(self, FTP_VIOLATION, 3, "Size is out of accepted range; req='%s' size='%ld'", "ALLO", num1); z_proxy_return(self, FTP_REQ_REJECT); } if (*endptr == 0) z_proxy_return(self, FTP_REQ_ACCEPT); if (strlen(endptr) >= 4 && endptr[0] == ' ' && endptr[1] == 'R' && endptr[2] == ' ' && endptr[3] != ' ') { str = endptr + 3; num2 = strtol(str, &endptr, 10); if (num2 < 0 || ((num2 == LONG_MAX || num2 == LONG_MIN) && errno == ERANGE)) { z_proxy_log(self, FTP_VIOLATION, 3, "Record number is out of accepted range; req='%s' size='%ld'", "ALLO", num2); z_proxy_return(self, FTP_REQ_REJECT); } if (*endptr == 0) z_proxy_return(self, FTP_REQ_ACCEPT); } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } /*LOG This message indicates that the parameter of the ALLO command is invalid and Zorp rejects the request. */ z_proxy_log(self, FTP_VIOLATION, 2, "Error parsing command (ALLO); param='%s'", self->request_param->str); z_proxy_return(self, FTP_REQ_REJECT); } guint ftp_command_parse_REIN(FtpProxy *self) { z_proxy_enter(self); if (self->auth_tls_ok[EP_CLIENT]) { /* we're using TLS and thus do not support REIN */ z_proxy_log(self, FTP_INFO, 4, "REIN command is not allowed in FTPS mode;"); SET_ANSWER(MSG_COMMAND_NOT_IMPLEMENTED); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_answer_REIN(FtpProxy *self) { switch (self->answer_cmd->str[0]) { case '1': return FTP_NOOP; case '2': /* FIXME: ughh... I can't see how this code is relevant here ... */ /* REFIX: This code set the state before login. */ ftp_proto_state_set(self, FTP_STATE_LOGIN); g_string_assign(self->username, ""); g_string_assign(self->password, ""); break; } return FTP_RSP_ACCEPT; } guint ftp_command_parse_REST(FtpProxy *self) { guint ret; ret = ftp_command_parse_string(self); if (ret == FTP_REQ_ACCEPT) if (self->request_param->len == 0) ret = FTP_REQ_REJECT; return ret; } static GHashTable * ftp_process_feature_list(FtpProxy *self, GList *incoming) { GHashTable *filtered; GList *i; z_proxy_enter(self); filtered = g_hash_table_new(g_str_hash, g_str_equal); i = incoming; while (i) { gchar *item = (gchar *) i->data; gint verdict; verdict = ftp_policy_feature_hash_search(self, item); if (verdict == FTP_FEATURE_ACCEPT) g_hash_table_insert(filtered, item, NULL); i = g_list_next(i); } /* we have the list of accepted features in 'filtered', now insert all new values according to the policy */ ftp_policy_feature_hash_handle_insert(self, filtered); /* we also have some hard-coded rules depending on FTPS settings: * - client: != ACCEPT_STARTTLS / server *: we have to remove 'AUTH TLS', 'PROT' and 'PBSZ' * - client: ACCEPT_STARTTLS / server != FORWARD_STARTTLS: we have to add 'AUTH TLS', * 'PBSZ' and 'PROT' */ if (self->super.ssl_opts.security[EP_CLIENT] != PROXY_SSL_SEC_ACCEPT_STARTTLS) { g_hash_table_remove(filtered, "AUTH TLS"); g_hash_table_remove(filtered, "PROT"); g_hash_table_remove(filtered, "PBSZ"); } else if ((self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) && ((self->super.ssl_opts.security[EP_SERVER] != PROXY_SSL_SEC_FORWARD_STARTTLS) || (self->transparent_mode == FALSE))) { g_hash_table_insert(filtered, "AUTH TLS", NULL); g_hash_table_insert(filtered, "PROT", NULL); g_hash_table_insert(filtered, "PBSZ", NULL); } z_proxy_return(self, filtered); } static void ftp_feature_add_cb(gpointer _key, gpointer _value G_GNUC_UNUSED, gpointer user_data) { gchar *key = (gchar *) _key; GString *str = (GString *) user_data; g_string_append(str, key); g_string_append(str, "\n"); } guint ftp_command_parse_FEAT(FtpProxy *self) { GHashTable *features = NULL; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_LOGIN: case FTP_STATE_LOGIN_U: case FTP_STATE_LOGIN_P: case FTP_STATE_CONVERSATION: g_string_assign(self->request_param, ""); break; case FTP_STATE_PRECONNECT: /* we're in non-transparent mode, generate a proxy answer */ features = ftp_process_feature_list(self, NULL); self->answer_code = 211; g_string_assign(self->answer_cmd, "211"); g_string_assign(self->answer_param, "Features:\n"); g_hash_table_foreach(features, ftp_feature_add_cb, self->answer_param); g_string_append(self->answer_param, "End"); g_hash_table_destroy(features); ftp_proto_state_set(self, FTP_STATE_PRECONNECT_FEAT); z_proxy_return(self, FTP_PROXY_ANS); break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_FEAT(FtpProxy *self) { gchar **lines; GList *features = NULL; GHashTable *filtered = NULL; z_proxy_enter(self); if (self->answer_code != 211) { /* If it was an error, and FTPS is enabled on the client side but disabled on the * server, we have to generate a fake list of features containing AUTH TLS, etc. * Thus, we need to change the answer code to 221. */ if ((self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) && (self->super.ssl_opts.security[EP_SERVER] != PROXY_SSL_SEC_FORWARD_STARTTLS)) { self->answer_code = 211; g_string_assign(self->answer_cmd, "211"); } else { /* otherwise, we simply accept the server's answer */ z_proxy_return(self, FTP_RSP_ACCEPT); } } lines = g_strsplit(self->answer_param->str, "\n", 255); if (lines != NULL && lines[0] != NULL) { gint i = 1; gchar *line; while ((line = lines[i++]) != NULL) { if (line[0] != ' ') continue; /* skip space */ line++; z_proxy_log(self, FTP_RESPONSE, 6, "Feature parsed; feature='%s'", line); features = g_list_append(features, line); } } /* do policy-driven filter/insert/remove */ filtered = ftp_process_feature_list(self, features); g_list_free(features); /* finally, replace the old answer_param with what we have */ g_string_assign(self->answer_param, "Features:\n"); g_hash_table_foreach(filtered, ftp_feature_add_cb, self->answer_param); g_string_append(self->answer_param, "End"); g_hash_table_destroy(filtered); if (lines != NULL) g_strfreev(lines); z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_AUTH(FtpProxy *self) { gboolean non_transparent = FALSE; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_PRECONNECT: case FTP_STATE_PRECONNECT_FEAT: non_transparent = TRUE; /* continue */ case FTP_STATE_LOGIN: /* FIXME: only the "TLS" method is supported at the moment */ if (g_ascii_strcasecmp(self->request_param->str, "TLS")) { z_proxy_log(self, FTP_ERROR, 3, "Unsupported authentication method; method='%s'", self->request_param->str); SET_ANSWER(MSG_COMMAND_NOT_IMPLEMENTED); z_proxy_return(self, FTP_REQ_REJECT); } /* we don't allow AUTH TLS more than once */ if (self->auth_tls_ok[EP_CLIENT]) { z_proxy_log(self, FTP_VIOLATION, 3, "AUTH TLS command is allowed only in plain-text mode;"); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } /* based on the client/server SSL settings, we do the following: * - client ACCEPT_STARTTLS / server FORWARD_STARTTLS: we forward * the request as is * - client !ACCEPT_STARTTLS / server *: we reject the request * - client ACCEPT_STARTTLS / server !FORWARD_STARTTLS: return success * to the client and don't forward the request */ if (self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) { if (!non_transparent && (self->super.ssl_opts.security[EP_SERVER] == PROXY_SSL_SEC_FORWARD_STARTTLS)) /* nothing to do, we do handshake only if the server agrees to do so, * but that is handled in the _answer_AUTH function */ break; else { /* we're either in non-transparent mode or must not forward AUTH TLS */ gboolean res; /* return success to the client right away */ z_proxy_log(self, FTP_INFO, 3, "Zorp is configured for non-transparent operation or client-only FTPS, accepting request;"); SET_ANSWER(MSG_AUTH_TLS_SUCCESSFUL); ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); res = z_proxy_ssl_request_handshake(&self->super, EP_CLIENT, non_transparent); if (!res) { z_proxy_log(self, FTP_ERROR, 2, "Client-side SSL handshake failed, terminating session;"); self->auth_tls_ok[EP_CLIENT] = FALSE; self->state = FTP_QUIT; } else self->auth_tls_ok[EP_CLIENT] = TRUE; if (self->ftp_state != FTP_STATE_LOGIN) { /* non-transparent mode */ ftp_proto_state_set(self, FTP_STATE_PRECONNECT_AUTH); } else { /* FIXME: this is normally done in ftp_process_request(), but we need to * do the SSL handshake *after* returning the proxy answer to the client */ if (self->state == FTP_SERVER_TO_CLIENT) { ftp_state_set(self, EP_CLIENT); self->state = FTP_CLIENT_TO_SERVER; } else if (self->state == FTP_NT_SERVER_TO_PROXY) { ftp_state_set( self, EP_CLIENT); self->state = FTP_NT_CLIENT_TO_PROXY; } } z_proxy_return(self, FTP_NOOP); } } else { /* reject the request */ SET_ANSWER(MSG_COMMAND_NOT_IMPLEMENTED); z_proxy_return(self, FTP_REQ_REJECT); } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_AUTH(FtpProxy *self) { gboolean res; z_proxy_enter(self); /* we can get here only in the following case, all others should have been handled in * ftp_command_parse_AUTH(): * - client ACCEPT_STARTTLS / server FORWARD_STARTTLS: do handshake on both * sides if the server accepted the request */ g_assert((self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) && (self->super.ssl_opts.security[EP_SERVER] == PROXY_SSL_SEC_FORWARD_STARTTLS)); switch (self->answer_code) { case 234: ftp_answer_write_with_setup(self, self->answer_cmd->str, self->answer_param->str); z_proxy_log(self, FTP_INFO, 3, "Server accepted TLS authentication, starting handshake;"); res = z_proxy_ssl_request_handshake(&self->super, EP_SERVER, FALSE); if (res) res = z_proxy_ssl_request_handshake(&self->super, EP_CLIENT, FALSE); if (!res) { z_proxy_log(self, FTP_ERROR, 2, "SSL handshake failed, terminating session;"); self->state = FTP_QUIT; } else { self->auth_tls_ok[EP_CLIENT] = self->auth_tls_ok[EP_SERVER] = TRUE; } z_proxy_return(self, FTP_NOOP); break; default: break; } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_PBSZ(FtpProxy *self) { z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: case FTP_STATE_PRECONNECT_AUTH: if (strcmp(self->request_param->str, "0") != 0) { z_proxy_log(self, FTP_VIOLATION, 3, "PBSZ parameter must be zero; param='%s'", self->request_param->str); SET_ANSWER(MSG_PBSZ_PARAMETER_INVALID); z_proxy_return(self, FTP_REQ_REJECT); } /* must have been preceded by successful AUTH TLS */ if (!self->auth_tls_ok[EP_CLIENT]) { z_proxy_log(self, FTP_VIOLATION, 3, "PBSZ must be preceded by a successful AUTH TLS command;"); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } if (self->ftp_state == FTP_STATE_PRECONNECT_AUTH) { /* non-transparent mode */ self->client_sent_pbsz = TRUE; ftp_proto_state_set(self, FTP_STATE_PRECONNECT_PBSZ); SET_ANSWER(MSG_PBSZ_SUCCESSFUL); z_proxy_return(self, FTP_PROXY_ANS); } else { /* * based on the client/server SSL settings, we do the following: * - client ACCEPT_STARTTLS / server !FORWARD_STARTTLS: return success to the client * and don't forward the request * - in all other cases we forward the request to the server */ if ((self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) && (self->super.ssl_opts.security[EP_SERVER] != PROXY_SSL_SEC_FORWARD_STARTTLS)) { SET_ANSWER(MSG_PBSZ_SUCCESSFUL); z_proxy_return(self, FTP_PROXY_ANS); } } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_parse_PROT(FtpProxy *self) { gboolean prot_level_private = FALSE; z_proxy_enter(self); switch (self->ftp_state) { case FTP_STATE_CONVERSATION: case FTP_STATE_PRECONNECT_PBSZ: if (g_ascii_strcasecmp(self->request_param->str, "P") && g_ascii_strcasecmp(self->request_param->str, "C")) { z_proxy_log(self, FTP_VIOLATION, 3, "PROT parameter must be either 'P' or 'C'; param='%s'", self->request_param->str); SET_ANSWER(MSG_PROT_PARAMETER_INVALID); z_proxy_return(self, FTP_REQ_REJECT); } /* must have been preceded by successful AUTH TLS */ if (!self->auth_tls_ok[EP_CLIENT]) { z_proxy_log(self, FTP_VIOLATION, 3, "PROT must be preceded by a successful AUTH TLS command;"); SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_REQ_REJECT); } if (g_ascii_strcasecmp(self->request_param->str, "P") == 0) prot_level_private = TRUE; if (self->ftp_state == FTP_STATE_PRECONNECT_PBSZ) { self->data_protection_enabled[EP_CLIENT] = prot_level_private; ftp_proto_state_set(self, FTP_STATE_PRECONNECT_PROT); SET_ANSWER(MSG_PROT_SUCCESSFUL); z_proxy_return(self, FTP_PROXY_ANS); } else /* * based on the client/server SSL settings, we do the following: * - client ACCEPT_STARTTLS / server !FORWARD_STARTTLS: return success to the client * and don't forward the request * - in all other cases we forward the request to the server */ if ((self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) && (self->super.ssl_opts.security[EP_SERVER] != PROXY_SSL_SEC_FORWARD_STARTTLS)) { self->data_protection_enabled[EP_CLIENT] = prot_level_private; SET_ANSWER(MSG_PROT_SUCCESSFUL); z_proxy_return(self, FTP_PROXY_ANS); } else { /* we temporary set data_protection_enabled according to the parameter, * in case the server doesn't accept the request we'll clear this */ if (self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) self->data_protection_enabled[EP_CLIENT] = prot_level_private; if (self->super.ssl_opts.security[EP_SERVER] == PROXY_SSL_SEC_FORWARD_STARTTLS) self->data_protection_enabled[EP_SERVER] = prot_level_private; } break; default: SET_ANSWER(MSG_COMMAND_NOT_ALLOWED_HERE); z_proxy_return(self, FTP_RSP_REJECT); } z_proxy_return(self, FTP_REQ_ACCEPT); } guint ftp_command_answer_PROT(FtpProxy *self) { z_proxy_enter(self); switch (self->answer_code) { case 200: z_proxy_log(self, FTP_INFO, 3, "Enabling SSL protection for data channels;"); break; default: /* in all other cases we disable data channel protection */ self->data_protection_enabled[EP_CLIENT] = self->data_protection_enabled[EP_SERVER] = FALSE; break; } z_proxy_return(self, FTP_RSP_ACCEPT); } guint ftp_command_parse_CCC(FtpProxy *self) { z_proxy_enter(self); z_proxy_log(self, FTP_INFO, 4, "CCC request is not implemented, rejecting;"); SET_ANSWER(MSG_COMMAND_NOT_IMPLEMENTED); z_proxy_return(self, FTP_REQ_REJECT); } zorp-3.9.5/modules/ftp/ftpcmd.h000066400000000000000000000064521172670260400164250ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #ifndef __FTP_PARSE_H #define __FTP_PARSE_H /* declare ftp command parser functions */ guint ftp_command_parse_noarg (FtpProxy *self); guint ftp_command_parse_path (FtpProxy *self); guint ftp_command_answer_path (FtpProxy *self); guint ftp_command_parse_string (FtpProxy *self); guint ftp_command_parse_ABOR (FtpProxy *self); guint ftp_command_answer_ABOR (FtpProxy *self); guint ftp_command_parse_HELP (FtpProxy *self); guint ftp_command_parse_MODE (FtpProxy *self); guint ftp_command_parse_PASS (FtpProxy *self); guint ftp_command_answer_PASS (FtpProxy *self); guint ftp_command_parse_ACCT (FtpProxy *self); guint ftp_command_answer_ACCT (FtpProxy *self); guint ftp_command_parse_PASV (FtpProxy *self); guint ftp_command_answer_PASV (FtpProxy *self); guint ftp_command_parse_PORT (FtpProxy *self); guint ftp_command_answer_PORT (FtpProxy *self); guint ftp_command_parse_EPSV (FtpProxy *self); guint ftp_command_answer_EPSV (FtpProxy *self); guint ftp_command_parse_EPRT (FtpProxy *self); guint ftp_command_answer_EPRT (FtpProxy *self); guint ftp_command_parse_QUIT (FtpProxy *self); guint ftp_command_answer_QUIT (FtpProxy *self); guint ftp_command_parse_STRU (FtpProxy *self); guint ftp_command_parse_TYPE (FtpProxy *self); guint ftp_command_parse_USER (FtpProxy *self); guint ftp_command_answer_USER (FtpProxy *self); guint ftp_command_answer_RNFR (FtpProxy *self); guint ftp_command_parse_RNTO (FtpProxy *self); guint ftp_command_parse_ALLO (FtpProxy *self); guint ftp_command_parse_REIN (FtpProxy *self); guint ftp_command_answer_REIN (FtpProxy *self); guint ftp_command_parse_REST (FtpProxy *self); guint ftp_command_parse_FEAT (FtpProxy *self); guint ftp_command_answer_FEAT (FtpProxy *self); guint ftp_command_parse_AUTH (FtpProxy *self); guint ftp_command_answer_AUTH (FtpProxy *self); guint ftp_command_parse_PBSZ (FtpProxy *self); guint ftp_command_parse_PROT (FtpProxy *self); guint ftp_command_answer_PROT (FtpProxy *self); guint ftp_command_parse_CCC (FtpProxy *self); #endif zorp-3.9.5/modules/ftp/ftpdata.c000066400000000000000000000210221172670260400165540ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "ftp.h" /* transfer states */ #define FTP_DW_INITIAL 0 #define FTP_DW_WRITE_PREAMBLE 1 #define FTP_DW_WRITE_DATA 2 typedef struct _FtpTransfer { ZTransfer2 super; guint dst_write_state; } FtpTransfer; extern ZClass FtpTransfer__class; /* FtpTransfer implementation */ static GIOStatus ftp_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize count, gsize *bytes_read, GError **err) { FtpProxy *owner = (FtpProxy *) s->owner; GIOStatus res; z_proxy_enter(owner); res = z_stream_read(stream, buf, count, bytes_read, err); z_proxy_return(owner, res); } static GIOStatus ftp_transfer_dst_write_preamble(FtpTransfer *self G_GNUC_UNUSED, ZStream *stream G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { GIOStatus res = G_IO_STATUS_NORMAL; FtpProxy *owner = (FtpProxy *) self->super.owner; if (owner->preamble) { if (!ftp_answer_write(owner, owner->preamble)) res = G_IO_STATUS_ERROR; g_free(owner->preamble); owner->preamble = NULL; } return res; } static GIOStatus ftp_transfer_dst_write(ZTransfer2 *s, ZStream *stream, const gchar *buf, gsize count, gsize *bytes_written, GError **err) { FtpTransfer *self = Z_CAST(s, FtpTransfer); GIOStatus res = G_IO_STATUS_NORMAL; *bytes_written = 0; if (self->dst_write_state == FTP_DW_INITIAL) self->dst_write_state = FTP_DW_WRITE_PREAMBLE; if (self->dst_write_state == FTP_DW_WRITE_PREAMBLE) { /* take care about the preamble (request/response itself) */ res = ftp_transfer_dst_write_preamble(self, stream, err); if (res != G_IO_STATUS_NORMAL) { goto propagate_exit; } self->dst_write_state = FTP_DW_WRITE_DATA; } res = z_stream_write(stream, buf, count, bytes_written, err); propagate_exit: return res; } GIOStatus ftp_transfer_dst_shutdown(ZTransfer2 *s, ZStream *stream, GError **err) { FtpTransfer *self = Z_CAST(s, FtpTransfer); GIOStatus res = G_IO_STATUS_NORMAL; if (self->dst_write_state == FTP_DW_INITIAL) self->dst_write_state = FTP_DW_WRITE_PREAMBLE; if (self->super.stack_decision == ZV_ACCEPT) { if (self->dst_write_state == FTP_DW_WRITE_PREAMBLE) { /* take care about the preamble (request/response itself) */ res = ftp_transfer_dst_write_preamble(self, stream, err); self->dst_write_state = FTP_DW_WRITE_DATA; } } return res; } static gboolean ftp_transfer_stack_proxy(ZTransfer2 *s, ZStackedProxy **stacked) { FtpTransfer *self = Z_CAST(s, FtpTransfer); ZPolicyObj *proxy_stack_tuple = NULL, *stack_object = NULL; gint stack_type = FTP_STK_NONE; gboolean called; gboolean success = FALSE; /* query python for a stacked proxy */ z_policy_lock(self->super.owner->thread); proxy_stack_tuple = z_policy_call(self->super.owner->handler, "requestStack", NULL, &called, self->super.owner->session_id); if (called && !proxy_stack_tuple) { goto unref_unlock; } if (!z_policy_var_parse(proxy_stack_tuple, "(iO)", &stack_type, &stack_object)) { /*LOG This message indicates that the request_stack or response_stack hash contains an invalid stacking tuple. It should contain a (stack_type, proxy_class) tuple. Check your Zorp configuration. */ z_proxy_log(self->super.owner, FTP_POLICY, 3, "Invalid stacking tuple returned by policy;"); goto unref_unlock; } if (stack_type < FTP_STK_NONE || stack_type > FTP_STK_DATA) { /*LOG This message indicates that the request_stack or response_stack hash contains an invalid stacking type. Check your Zorp configuration. */ z_proxy_log(self->super.owner, FTP_POLICY, 3, "Invalid stacking type; type='%d'", stack_type); stack_type = FTP_STK_NONE; goto unref_unlock; } /* NOTE: FTP_STK_POLICY is never returned here, it is handled by the policy layer */ success = TRUE; switch (stack_type) { case FTP_STK_NONE: break; case FTP_STK_DATA: default: success = z_proxy_stack_object(s->owner, stack_object, stacked, NULL); break; } unref_unlock: z_policy_var_unref(proxy_stack_tuple); z_policy_unlock(self->super.owner->thread); return success; } static FtpTransfer * ftp_transfer_new(FtpProxy *owner, ZStream *from_stream, ZStream *to_stream) { FtpTransfer *self; z_proxy_enter(owner); self = Z_CAST(z_transfer2_new(Z_CLASS(FtpTransfer), &owner->super, owner->poll, from_stream, to_stream, owner->buffer_size, owner->timeout, 0), FtpTransfer); z_proxy_return(owner, self); } ZTransfer2Funcs ftp_transfer_funcs = { { Z_FUNCS_COUNT(ZTransfer2), NULL, }, .src_read = ftp_transfer_src_read, .dst_write = ftp_transfer_dst_write, .src_shutdown = NULL, .dst_shutdown = ftp_transfer_dst_shutdown, .stack_proxy = ftp_transfer_stack_proxy, .setup = NULL, .run = NULL, .progress = NULL }; Z_CLASS_DEF(FtpTransfer, ZTransfer2, ftp_transfer_funcs); gboolean ftp_data_transfer(FtpProxy *self, ZStream *from_stream, ZStream *to_stream) { FtpTransfer *t; gboolean res = TRUE; ZTransfer2Result tr; z_proxy_enter(self); t = ftp_transfer_new(self, from_stream, to_stream); if (!t || !z_transfer2_start(&t->super)) { /*LOG This message indicates that the processed request was invalid, and the data transfer failed. */ z_proxy_log(self, FTP_ERROR, 2, "Invalid request, data transfer failed;"); SET_ANSWER(MSG_DATA_TRANSFER_FAILED); res = FALSE; goto exit; } self->transfer = &t->super; do { tr = z_transfer2_run(&t->super); } while (tr == ZT2_RESULT_SUSPENDED); self->transfer = NULL; res = (tr != ZT2_RESULT_FAILED) && (tr != ZT2_RESULT_ABORTED); if (!res) { /* transfer was not successful */ /*LOG This message reports that the data transfer failed. */ z_proxy_log(self, FTP_ERROR, 2, "Data transfer failed;"); SET_ANSWER(MSG_DATA_TRANSFER_FAILED); } /* transfer was successful, check if the stacked proxy told us something important */ if (t->super.stack_decision != ZV_ACCEPT) { res = FALSE; /*LOG This message indicates that the stacked proxy returned the specified verdict about the content. Check the stacked proxy log for further information. */ z_proxy_log(self, FTP_ERROR, 2, "Stacked proxy decision; verdict='%d', info='%s'", t->super.stack_decision, t->super.stack_info->str); SET_ANSWER(MSG_DATA_TRANSFER_FAILED); if (t->super.stack_info->len) g_string_append_printf(self->answer_param, " (%s)", t->super.stack_info->str); } else if (res) { /*LOG This message indicates that the stacked proxy accepted the content. */ z_proxy_log(self, FTP_DEBUG, 6, "Stacked proxy accepted data;"); } exit: z_stream_shutdown(from_stream, SHUT_RDWR, NULL); z_stream_close(from_stream, NULL); z_stream_unref(from_stream); z_stream_shutdown(to_stream, SHUT_RDWR, NULL); z_stream_close(to_stream, NULL); z_stream_unref(to_stream); if (t) z_object_unref(&t->super.super); z_proxy_return(self, res); } zorp-3.9.5/modules/ftp/ftphash.c000066400000000000000000000152141172670260400165740ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "ftp.h" #include "ftphash.h" #include "ftpcmd.h" // APASV LPRT LPSV .... ??? // FTP command hash: command, flag, c_function, a_function, need data conn. static struct _FtpInternalCommand ftp_commands[] = { /* rfc959 */ {"ABOR", ftp_command_parse_ABOR, ftp_command_answer_ABOR, 0}, {"ACCT", ftp_command_parse_ACCT, ftp_command_answer_ACCT, 0}, {"ALLO", ftp_command_parse_ALLO, NULL, 0}, {"APPE", ftp_command_parse_path, ftp_command_answer_path, 2}, {"CDUP", ftp_command_parse_noarg, NULL, 0}, {"CWD", ftp_command_parse_path, NULL, 0}, {"DELE", ftp_command_parse_path, NULL, 0}, {"HELP", ftp_command_parse_HELP, NULL, 0}, {"LIST", ftp_command_parse_path, ftp_command_answer_path, 1}, {"MKD", ftp_command_parse_path, NULL, 0}, {"MODE", ftp_command_parse_MODE, NULL, 0}, {"NLST", ftp_command_parse_path, ftp_command_answer_path, 1}, {"NOOP", ftp_command_parse_noarg, NULL, 0}, {"PASS", ftp_command_parse_PASS, ftp_command_answer_PASS, 0}, {"PASV", ftp_command_parse_PASV, ftp_command_answer_PASV, 0}, {"PBSZ", ftp_command_parse_PBSZ, NULL, 0}, {"PORT", ftp_command_parse_PORT, ftp_command_answer_PORT, 0}, {"PROT", ftp_command_parse_PROT, ftp_command_answer_PROT, 0}, {"PWD", ftp_command_parse_noarg, NULL, 0}, {"REST", ftp_command_parse_REST, NULL, 0}, {"RETR", ftp_command_parse_path, ftp_command_answer_path, 1}, {"RMD", ftp_command_parse_path, NULL, 0}, {"RNFR", ftp_command_parse_path, ftp_command_answer_RNFR, 0}, {"RNTO", ftp_command_parse_RNTO, NULL, 0}, {"QUIT", ftp_command_parse_QUIT, ftp_command_answer_QUIT, 0}, {"REIN", ftp_command_parse_REIN, NULL, 0}, {"SITE", ftp_command_parse_string, NULL, 0}, {"SMNT", ftp_command_parse_path, NULL, 0}, {"STAT", ftp_command_parse_path, NULL, 0}, {"STOR", ftp_command_parse_path, ftp_command_answer_path, 2}, {"STOU", ftp_command_parse_path, ftp_command_answer_path, 2}, {"STRU", ftp_command_parse_STRU, NULL, 0}, {"SYST", ftp_command_parse_noarg, NULL, 0}, {"TYPE", ftp_command_parse_TYPE, NULL, 0}, {"USER", ftp_command_parse_USER, ftp_command_answer_USER, 0}, /* rfc775 */ {"XCUP", ftp_command_parse_noarg, NULL, 0}, {"XCWD", ftp_command_parse_path, NULL, 0}, {"XMKD", ftp_command_parse_path, NULL, 0}, {"XPWD", ftp_command_parse_noarg, NULL, 0}, {"XRMD", ftp_command_parse_path, NULL, 0}, /* rfc2389 */ {"FEAT", ftp_command_parse_FEAT, ftp_command_answer_FEAT, 0}, {"AUTH", ftp_command_parse_AUTH, ftp_command_answer_AUTH, 0}, {"CCC", ftp_command_parse_CCC, NULL, 0}, /* rfc2428 */ {"EPRT", ftp_command_parse_EPRT, ftp_command_answer_EPRT, 0}, {"EPSV", ftp_command_parse_EPSV, ftp_command_answer_EPSV, 0}, /* rfc3659 */ {"MLSD", ftp_command_parse_path, ftp_command_answer_path, 1}, {"MLST", ftp_command_parse_path, NULL, 0}, #if 0 /* rfc2228 */ {"ADAT", ftp_command_parse_sftp, NULL, 0}, {"AUTH", ftp_command_parse_sftp, NULL, 0}, {"CONF", ftp_command_parse_sftp, NULL, 0}, {"ENC", ftp_command_parse_sftp, NULL, 0}, {"MIC", ftp_command_parse_sftp, NULL, 0}, {"PBSZ", ftp_command_parse_sftp, NULL, 0}, {"PROT", ftp_command_parse_sftp, NULL, 0}, /* rfc2389 */ {"OPTS", ftp_command_parse_noarg, NULL, 0}, /* rfc1579 */ {"APSV", ftp_command_parse_PASV, NULL, 0}, /* rfc1545 */ {"LPRT", ftp_command_parse_string, NULL, 0}, {"LPSV", ftp_command_parse_string, NULL, 0}, /* ???? */ {"MDTM", ftp_command_parse_string, NULL, 0}, #endif {NULL, NULL, NULL, 0} }; // Function: ftp_command_hash_create // In: FtpProxy* proxy pointer // Out: - // Desc: create and fill the hashtable void ftp_command_hash_create(void) { int i; ftp_command_hash = g_hash_table_new(g_str_hash, g_str_equal); i = 0; while (ftp_commands[i].name != NULL) { g_hash_table_insert(ftp_command_hash, ftp_commands[i].name, &ftp_commands[i]); i++; } } // Function: ftp_command_hash_get // In: gchar command // Out: gpointer value // Desc: look up the command in the ftp_command_hash // return: a pointer to the structure FtpInternalCommand * ftp_command_hash_get(gchar * name) { FtpInternalCommand *wp = g_hash_table_lookup(ftp_command_hash, name); return wp; } zorp-3.9.5/modules/ftp/ftphash.h000066400000000000000000000031511172670260400165760ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_FTP_FTPHASH_H #define ZORP_MODULES_FTP_FTPHASH_H struct _FtpInternalCommand; struct _FtpProxy; typedef guint (*FtpCmdFunction)(struct _FtpProxy *self); typedef struct _FtpInternalCommand { char *name; // guint state; FtpCmdFunction parse; FtpCmdFunction answer; int need_data; } FtpInternalCommand; extern GHashTable *ftp_command_hash; void ftp_command_hash_create(void); FtpInternalCommand *ftp_command_hash_get(gchar *name); #endif zorp-3.9.5/modules/ftp/ftpmsg.c000066400000000000000000000071741172670260400164450ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "ftp.h" // Hard-coded answers Ftp_message ftp_know_messages[] = { {"501", "Username format invalid."}, {"501", "Hostname too long."}, {"530", "Already logged in."}, {"421", "Service not available, remote server has closed connection."}, {"220", "Welcome in Zorp FTP proxy authentication module!\nPlease authenticate yourself!\nAuthentication form:\n USER @\n PASS \n"}, {"500", "Command line too long."}, {"500", "Line must be terminated with a CR LF pair."}, {"501", "Username too long."}, {"503", "Login with USER first."}, {"501", "Password too long."}, {"331", "Username and host okay, send your password."}, {"500", "Command is not allowed at this time"}, {"500", "Invalid parameters"}, {"221", "Goodbye"}, {"500", "Missing parameters"}, {"504", "Command not implemented for that parameter"}, {"500", "Command not recognized"}, {"500", "Answer error"}, {"500", "Error processing PORT command"}, {"500", "Error parsing PASV parameters"}, {"500", "Error processing PASV command"}, {"500", "Error parsing PORT parameters"}, {"200", "PORT command succesfull"}, {"500", "RNFR must precedence RNTO"}, {"500", "Error parsing command"}, {"500", "Connection timed out"}, {"500", "Error parsing EPSV parameters"}, {"500", "Error parsing EPRT parameters"}, {"550", "Data transfer failed"}, {"220", "Welcome in Zorp FTP proxy authentication module!\nPlease authenticate yourself!\nAuthentication form:\n" " USER @@[:]:@\n PASS \n" "or\n USER @@[:]\n PASS @\n"}, {"501", "Password format is invalid."}, {"234", "AUTH TLS successful"}, {"200", "PBSZ successful"}, {"501", "Buffer size invalid"}, {"504", "Invalid protection level"}, {"200", "Protection level set"}, {"502", "Command not implemented"}, {"530", "Login failed: SSL handshake failed with the server"}, {"530", "Login failed: the subject of the server's SSL certificate does not match the hostname"}, {"530", "Login failed: the server rejected the AUTH request"}, {"530", "Login failed: the server rejected the PBSZ request"}, {"530", "Login failed: the server rejected the PROT request"}, {"501", "Inband routing information invalid"}, }; zorp-3.9.5/modules/ftp/ftpmsg.h000066400000000000000000000024561172670260400164500ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_FTP_FTPMSG_H #define ZORP_MODULES_FTP_FTPMSG_H typedef struct _Ftp_message { char *code; char *long_desc; } Ftp_message; #endif zorp-3.9.5/modules/ftp/ftpolicy.c000066400000000000000000000377211172670260400167770ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "ftp.h" #include "ftpolicy.h" gboolean ftp_policy_bounce_check(FtpProxy *self, guint side, ZSockAddr *remote, gboolean connect) { PyObject *zsock; gboolean called; ZPolicyObj *res; gboolean ret; z_proxy_enter(self); z_policy_lock(self->super.thread); zsock = z_policy_sockaddr_new(remote); res = z_policy_call(self->super.handler, "bounceCheck", z_policy_var_build("(Oii)", zsock, side, connect), &called, self->super.session_id); if (!called) { z_policy_unlock(self->super.thread); z_proxy_return(self, TRUE); } if ((res == NULL) || !z_policy_var_parse(res, "i", &ret)) ret = FALSE; z_policy_var_unref(res); z_policy_var_unref(zsock); z_policy_unlock(self->super.thread); z_proxy_return(self, ret); } gboolean ftp_policy_parse_authinfo(FtpProxy *self, const gchar *cmd, GString *param) { gboolean called = FALSE; PyObject *result = NULL; PyObject *args = NULL; gboolean ret; z_proxy_enter(self); z_policy_lock(self->super.thread); args = z_policy_var_build("ss", cmd, param->str); result = z_policy_call(self->super.handler, "parseInbandAuth", args, &called, self->super.session_id); if (!called) { z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } if (result == NULL || !z_policy_var_parse(result, "i", &ret)) ret = FALSE; if (result) z_policy_var_unref(result); z_policy_unlock(self->super.thread); z_proxy_return(self, ret); } GHashTable * ftp_policy_command_hash_create(void) { GHashTable *tmp; tmp = g_hash_table_new(g_str_hash, g_str_equal); return tmp; } gboolean ftp_policy_command_hash_search(FtpProxy *self,gchar *command) { FtpCommandDescriptor *tmp; tmp = g_hash_table_lookup(self->policy_command_hash, command); return tmp != NULL; } gboolean ftp_hash_get_type(ZPolicyObj *tuple, guint *filter_type) { ZPolicyObj *tmp; if (!z_policy_seq_check(tuple)) { if (z_policy_var_parse(tuple, "i", filter_type)) return TRUE; /* not a sequence */ return FALSE; } tmp = z_policy_seq_getitem(tuple, 0); if (!z_policy_var_parse(tmp, "i", filter_type)) { /* policy syntax error */ z_policy_var_unref(tmp); return FALSE; } z_policy_var_unref(tmp); return TRUE; } guint ftp_policy_command_hash_do(FtpProxy *self) { guint ret; ZPolicyObj *res; ZPolicyObj *tmp; ZPolicyObj *command_where; unsigned int command_do; gchar work[10]; gchar *msg; int i; z_proxy_enter(self); tmp = g_hash_table_lookup(self->policy_command_hash, self->request_cmd->str); if (!tmp) { z_proxy_log(self, FTP_POLICY, 6, "Policy does not contain this request, trying the default; request='%s'", self->request_cmd->str); tmp = g_hash_table_lookup(self->policy_command_hash, "*"); } if (!tmp) { /*LOG This message indicates that the policy does not contain any setting for the given request and Zorp rejects the request. Check the 'request' attribute. */ z_proxy_log(self, FTP_POLICY, 5, "Policy does not contain this request, using hard-coded default; request='%s'", self->request_cmd->str); z_proxy_return(self, FTP_REQ_REJECT); } z_policy_lock(self->super.thread); if (!ftp_hash_get_type(tmp,&command_do)) { /*LOG This message indicates that the policy type is invalid for the given request and Zorp rejects the request. Check the 'request' attribute. */ z_proxy_log(self, FTP_POLICY, 1, "Policy type invalid; req='%s", self->request_cmd->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FTP_REQ_REJECT); } z_policy_unlock(self->super.thread); switch(command_do) { case FTP_REQ_ACCEPT: case FTP_REQ_ABORT: ret = command_do; break; case FTP_REQ_REJECT: z_policy_lock(self->super.thread); if (z_policy_var_parse(tmp, "(is)", &command_do, &msg)) { for (i = 0; i < 3; i++) work[i]=msg[i]; work[i]=0; g_string_assign(self->answer_cmd, work); g_string_assign(self->answer_param, &msg[i+1]); } ret = command_do; z_policy_unlock(self->super.thread); break; case FTP_REQ_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp,"(iO)",&command_do,&command_where)) { /*LOG This message indicates that the policy for the given request is invalid and Zorp rejects the request. Check the 'request' attribute. It is likely that the parameter for the FTP_REQ_POLICY is invalid. */ z_proxy_log(self, FTP_POLICY, 1, "Cannot parse policy line; req='%s'", self->request_cmd->str); ret = FTP_REQ_ABORT; } else { g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing command"); res = z_policy_call_object(command_where, z_policy_var_build("(s)",self->request_cmd->str), self->super.session_id); if (res == NULL) { /*LOG This message indicates that the callback for the given request policy is invalid and Zorp rejects the request. Check the 'request' attribute. It is likely that the parameter for the FTP_REQ_POLICY is invalid. */ z_proxy_log(self, FTP_POLICY, 1, "Error in policy calling; req='%s'", self->request_cmd->str); ret = FTP_REQ_ABORT; } else if (!z_policy_var_parse(res,"i",&ret)) { /*LOG This message indicates that the returned value of the callback for the given request policy is invalid and Zorp rejects the request. Check the callback function. */ z_proxy_log(self, FTP_POLICY, 1, "Can't parsing return code; command='%s'", self->request_cmd->str); ret = FTP_REQ_ABORT; } else { switch(ret) { case FTP_REQ_ACCEPT: case FTP_REQ_ABORT: case FTP_REQ_REJECT: break; case ZV_UNSPEC: case ZV_DROP: ret = FTP_REQ_REJECT; break; default: break; } } } z_policy_unlock(self->super.thread); break; default: ret = FTP_REQ_ABORT; break; } z_proxy_return(self, ret); } ZDimHashTable * ftp_policy_answer_hash_create(void) { ZDimHashTable *tmp; tmp = z_dim_hash_table_new(2, 2, DIMHASH_WILDCARD, DIMHASH_CONSUME); return tmp; } guint ftp_policy_answer_hash_do(FtpProxy *self) { guint ret; ZPolicyObj *res; ZPolicyObj *tmp; ZPolicyObj *answer_where; unsigned int answer_do; gchar key1[5]; gchar key2[5]; gchar *key[2]; gchar *msg; int i; gchar work[10]; z_proxy_enter(self); if (self->request_cmd->len > 0) g_snprintf(key1, sizeof(key1), "%s", self->request_cmd->str); else g_snprintf(key1, sizeof(key1), "Null"); g_snprintf(key2, sizeof(key2), "%s", self->answer_cmd->str); key[0] = key1; key[1] = key2; tmp = z_dim_hash_table_search(self->policy_answer_hash, 2, key); if (!tmp) { /*LOG This message indicates that the policy does not contain any setting for the given response and Zorp rejects the response. Check the 'response' attribute. */ z_proxy_log(self, FTP_POLICY, 5, "Policy does not contain this response, using hard-coded default; request='%s', response='%s", self->request_cmd->str, self->answer_cmd->str); z_proxy_return(self, FTP_RSP_REJECT); } z_policy_lock(self->super.thread); if (!ftp_hash_get_type(tmp, &answer_do)) { /*LOG This message indicates that the policy type is invalid for the given response and Zorp rejects the request. Check the 'request' attribute. */ z_proxy_log(self, FTP_POLICY, 1, "Answer type invalid; req='%s', rsp='%s'", self->request_cmd->str, self->answer_cmd->str); z_proxy_return(self, FTP_RSP_REJECT); } z_policy_unlock(self->super.thread); switch(answer_do) { case FTP_RSP_REJECT: ret = FTP_RSP_REJECT; z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp, "(is)", &answer_do, &msg)) { g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer"); } else { for(i = 0; i < 3; i++) work[i]=msg[i]; work[i]=0; g_string_assign(self->answer_cmd, work); g_string_assign(self->answer_param, &msg[i+1]); } z_policy_unlock(self->super.thread); break; case FTP_RSP_ACCEPT: ret = FTP_RSP_ACCEPT; break; case FTP_RSP_ABORT: ret = FTP_RSP_ABORT; z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp, "(is)", &answer_do, &msg)) { g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer"); } else { for(i = 0; i < 3; i++) work[i]=msg[i]; work[i]=0; g_string_assign(self->answer_cmd, work); g_string_assign(self->answer_param, &msg[i+1]); } z_policy_unlock(self->super.thread); break; case FTP_RSP_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp,"(iO)", &answer_do, &answer_where)) { /*LOG This message indicates that the policy for the given response is invalid and Zorp rejects the response. Check the 'response' attribute. It is likely that the parameter for the FTP_RSP_POLICY is invalid. */ z_proxy_log(self, FTP_POLICY, 1, "Bad policy line; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str); g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer (bad policy)"); ret = FTP_RSP_ABORT; } else { res = z_policy_call_object(answer_where, z_policy_var_build("(ss)", self->request_cmd->str, self->answer_cmd->str), self->super.session_id); if (res == NULL) { /*LOG This message indicates that the callback for the given response policy is invalid and Zorp rejects the response. Check the 'response' attribute. It is likely that the parameter for the FTP_RSP_POLICY is invalid. */ z_proxy_log(self, FTP_POLICY, 1, "Error in policy calling; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str); g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer (bad policy)"); ret = FTP_RSP_ABORT; } else if (!z_policy_var_parse(res, "i", &ret)) { /*LOG This message indicates that the returned value of the callback for the given response policy is invalid and Zorp rejects the response. Check the callback function. */ z_proxy_log(self, FTP_POLICY, 1, "Return code invalid from policy function; command='%s', answer='%s'", self->request_cmd->str, self->answer_cmd->str); g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer (bad policy)"); ret = FTP_RSP_ABORT; } else { switch(ret) { case FTP_RSP_ACCEPT: case FTP_RSP_REJECT: case FTP_RSP_ABORT: break; case ZV_DROP: case ZV_UNSPEC: ret = FTP_RSP_REJECT; break; default: g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer, connection dropped."); ret = FTP_RSP_ABORT; break; } } } z_policy_unlock(self->super.thread); break; default: g_string_assign(self->answer_cmd, "500"); g_string_assign(self->answer_param, "Error parsing answer, connection dropped."); ret = FTP_RSP_ABORT; break; } z_proxy_return(self, ret); } guint ftp_policy_feature_hash_search(struct _FtpProxy *self, const gchar *feature) { ZPolicyObj *res; guint verdict; gboolean valid; z_proxy_enter(self); res = g_hash_table_lookup(self->policy_features, feature); if (!res) res = g_hash_table_lookup(self->policy_features, "*"); if (!res) { /*LOG This message indicates that the policy does not contain any setting for the given feature and Zorp drops the feature. Check the 'features' attribute. */ z_proxy_log(self, FTP_POLICY, 5, "Policy does not contain this feature, dropping; feature='%s'", feature); z_proxy_return(self, FTP_FEATURE_DROP); } z_policy_lock(self->super.thread); valid = ftp_hash_get_type(res, &verdict); z_policy_unlock(self->super.thread); if (!valid) { /*LOG This message indicates that the policy type is invalid for the given feature and thus Zorp drops the feature. */ z_proxy_log(self, FTP_POLICY, 1, "Policy value invalid; feature='%s'", feature); z_proxy_return(self, FTP_FEATURE_DROP); } z_proxy_return(self, verdict); } static void ftp_policy_feature_hash_foreach_cb(gpointer _key, gpointer _value, gpointer user_data) { gchar *key = (gchar *) _key; ZPolicyObj *verdict = (ZPolicyObj *) _value; GHashTable *features = (GHashTable *) user_data; guint verdict_parsed; if (ftp_hash_get_type(verdict, &verdict_parsed) && (verdict_parsed == FTP_FEATURE_INSERT)) g_hash_table_insert(features, key, NULL); } void ftp_policy_feature_hash_handle_insert(struct _FtpProxy *self, GHashTable *features) { z_proxy_enter(self); z_policy_lock(self->super.thread); g_hash_table_foreach(self->policy_features, ftp_policy_feature_hash_foreach_cb, features); z_policy_unlock(self->super.thread); z_proxy_leave(self); } zorp-3.9.5/modules/ftp/ftpolicy.h000066400000000000000000000046461172670260400170040ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Andras Kis-Szabo * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_MODULES_FTP_FTPOLICY_H #define ZORP_MODULES_FTP_FTPOLICY_H struct _FtpProxy; typedef struct _FtpCommandDescriptor { unsigned int flags; char *event; } FtpCommandDescriptor; typedef struct _FtpAnswerDescriptor { unsigned int flags; char *event; } FTPAnswerDescriptor; gboolean ftp_policy_bounce_check(struct _FtpProxy *self, guint side, ZSockAddr *remote, gboolean connect); gboolean ftp_policy_parse_authinfo(struct _FtpProxy *self, const gchar *cmd, GString *param); GHashTable *ftp_policy_command_hash_create(void); gboolean ftp_policy_command_hash_search(struct _FtpProxy *self, gchar *command); guint ftp_policy_command_hash_do(struct _FtpProxy *self); ZDimHashTable *ftp_policy_answer_hash_create(void); guint ftp_policy_answer_hash_do(struct _FtpProxy *self); guint ftp_policy_feature_hash_search(struct _FtpProxy *self, const gchar *feature); void ftp_policy_feature_hash_handle_insert(struct _FtpProxy *self, GHashTable *features); #endif zorp-3.9.5/modules/ftp/tests/000077500000000000000000000000001172670260400161325ustar00rootroot00000000000000zorp-3.9.5/modules/ftp/tests/Makefile.am000066400000000000000000000002011172670260400201570ustar00rootroot00000000000000check_SCRIPTS = test_ftp_parse_inband_auth.sh TESTS = test_ftp_parse_inband_auth.sh EXTRA_DIST = test_ftp_parse_inband_auth.py zorp-3.9.5/modules/ftp/tests/test_ftp_parse_inband_auth.py000066400000000000000000000270261172670260400240710ustar00rootroot00000000000000#!/usr/bin/env python import unittest import os ftp_py_globals = {} def proxyLog(a, b, c, d): pass class TestParseFtpInbandAuth(unittest.TestCase): @staticmethod def newObject(): global ftp_py_globals return ftp_py_globals["AbstractFtpProxy"](None) def test_no_ats(self): o = self.newObject() self.assertFalse(o.parseInbandAuth('USER', 'fred')) def test_current_no_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@ftpserver.com')) self.assertTrue(o.parseInbandAuth('PASS', 'lightbringer')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, None) self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, 'lightbringer') self.assertEqual(o.proxy_password, None) def test_current_empty(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', '@')) self.assertEqual(o.username, '') self.assertEqual(o.proxy_username, None) self.assertEqual(o.hostname, '') self.assertEqual(o.hostport, None) self.assertEqual(o.password, None) self.assertEqual(o.proxy_password, None) def test_current_numeric_host(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@222')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, None) self.assertEqual(o.hostname, '222') self.assertEqual(o.hostport, None) self.assertEqual(o.password, None) self.assertEqual(o.proxy_password, None) def test_user_lowercase(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('user', 'fred@ftpserver.com')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, None) self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, None) self.assertEqual(o.proxy_password, None) def test_current_with_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@ftpserver.com:2020')) self.assertTrue(o.parseInbandAuth('PASS', 'lightbringer')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, None) self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, 2020) self.assertEqual(o.password, 'lightbringer') self.assertEqual(o.proxy_password, None) def test_current_empty_with_port(self): o = self.newObject() self.assertFalse(o.parseInbandAuth('USER', '@:')) def test_current_numeric_host_with_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@222:111')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, None) self.assertEqual(o.hostname, '222') self.assertEqual(o.hostport, 111) self.assertEqual(o.password, None) self.assertEqual(o.proxy_password, None) def test_full_no_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, 'ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_empty(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', '@@:@')) self.assertEqual(o.username, '') self.assertEqual(o.proxy_username, '') self.assertEqual(o.hostname, '') self.assertEqual(o.hostport, None) self.assertEqual(o.password, '') self.assertEqual(o.proxy_password, '') def test_full_numeric_host_no_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@222:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, '222') self.assertEqual(o.hostport, None) self.assertEqual(o.password, 'ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_with_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:666:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, 666) self.assertEqual(o.password, 'ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_empty_with_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', '@@::@')) self.assertEqual(o.username, '') self.assertEqual(o.proxy_username, '') self.assertEqual(o.hostname, '') #self.assertEqual(o.hostport, '') self.assertEqual(o.hostport, None) #self.assertEqual(o.password, '') self.assertEqual(o.password, ':') self.assertEqual(o.proxy_password, '') def test_full_numeric_host_with_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@222:666:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, '222') self.assertEqual(o.hostport, 666) self.assertEqual(o.password, 'ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_with_too_many_colons(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:666:ftp:pass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, 666) self.assertEqual(o.password, 'ftp:pass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_with_too_many_ats(self): o = self.newObject() self.assertFalse(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:666:ftppass@proxy@pass')) def test_full_with_too_low_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:0:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, '0:ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_with_too_high_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:65536:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, '65536:ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_full_with_non_numeric_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:666a:ftppass@proxypass')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, '666a:ftppass') self.assertEqual(o.proxy_password, 'proxypass') def test_half_user_no_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com')) self.assertTrue(o.parseInbandAuth('PASS', 'mew@mewmew')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, 'mew') self.assertEqual(o.proxy_password, 'mewmew') def test_half_user_with_port(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:999')) self.assertTrue(o.parseInbandAuth('PASS', 'foo@bar')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, 999) self.assertEqual(o.password, 'foo') self.assertEqual(o.proxy_password, 'bar') def test_half_user_with_bad_port(self): o = self.newObject() self.assertFalse(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com:0x12')) def test_pass_lowercase(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com')) self.assertTrue(o.parseInbandAuth('pass', 'spam@eggs')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, 'spam') self.assertEqual(o.proxy_password, 'eggs') def test_pass_no_ats(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com')) self.assertFalse(o.parseInbandAuth('pass', 'spam')) def test_pass_empty(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'fred@proxyfred@ftpserver.com')) self.assertTrue(o.parseInbandAuth('pass', '@')) self.assertEqual(o.username, 'fred') self.assertEqual(o.proxy_username, 'proxyfred') self.assertEqual(o.hostname, 'ftpserver.com') self.assertEqual(o.hostport, None) self.assertEqual(o.password, '') self.assertEqual(o.proxy_password, '') def test_pass_numeric_ip(self): o = self.newObject() self.assertTrue(o.parseInbandAuth('USER', 'ftp@127.0.0.1')) self.assertTrue(o.parseInbandAuth('PASS', 'user@')) self.assertEqual(o.username, 'ftp') self.assertEqual(o.hostname, '127.0.0.1') self.assertEqual(o.password, 'user@') self.assertEqual(o.proxy_username, None) self.assertEqual(o.proxy_password, None) stub = """ FALSE = 0 TRUE = 1 class Proxy(object): def __init__(self, session): self.username = None self.proxy_username = None self.hostname = None self.hostport = None self.password = None self.proxy_password = None """ def main(): # WARNING: hacks ahead try: path = os.environ['ZWA_MODULE_WORK_DIR'] + '/modules/ftp' # running from zwa make check/zwa sel (from build dir) except KeyError: path = '..' # probably started manually ftp_py_file = open(path + '/Ftp.py', 'rU') ftp_py_code = stub for line in ftp_py_file: if line.startswith('from'): continue if line.startswith('import'): continue ftp_py_code += line global ftp_py_globals exec ftp_py_code in ftp_py_globals ftp_py_globals["proxyLog"] = proxyLog unittest.main() main() zorp-3.9.5/modules/http/000077500000000000000000000000001172670260400151565ustar00rootroot00000000000000zorp-3.9.5/modules/http/Http.py000066400000000000000000003356641172670260400164700ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : Bazsi ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Proxy for the HyperText Transfer Protocol. The Http module defines the classes constituting the proxy for the HyperText Transfer Protocol (HTTP). HTTP is the protocol the Web is based on, therefore it is the most frequently used protocol on the Internet. It is used to access different kinds of content from the Web. The type of content retrieved via HTTP is not restricted, it can range from simple text files to hypertext files and multimedia formats like pictures, videos or audio files.
The HTTP protocol HTTP is an open application layer protocol for hypermedia information systems. It basically allows an open-ended set of methods to be applied to resources identified by Uniform Resource Identifiers (URIs).
Protocol elements HTTP is a text based protocol where a client sends a request comprising of a METHOD, an URI and associated meta information represented as MIME-like headers, and possibly a data attachment. The server responds with a status code, a set of headers, and possibly a data attachment. Earlier protocol versions perform a single transaction in a single TCP connection, HTTP/1.1 introduces persistency where a single TCP connection can be reused to perform multiple transactions. An HTTP method is a single word - usually spelled in capitals - instructing the server to apply a function to the resource specified by the URI. Commonly used HTTP methods are "GET", "POST" and "HEAD". HTTP method names are not restricted in any way, other HTTP based protocols (such as WebDAV) add new methods to the protocol while keeping the general syntax intact. Headers are part of both the requests and the responses. Each header consists of a name followed by a colon (':') and a field value. These headers are used to specify content-specific and protocol control information. The response to an HTTP request starts with an HTTP status line informing the client about the result of the operation and an associated message. The result is represented by three decimal digits, the possible values are defined in the HTTP RFCs.
Protocol versions The protocol has three variants, differentiated by their version number. Version 0.9 is a very simple protocol which allows a simple octet-stream to be transferred without any meta information (e.g.: no headers are associated with requests or responses). Version 1.0 introduces MIME-like headers in both requests and responses; headers are used to control both the protocol (e.g.: the "Connection" header) and to give information about the content being transferred (e.g.: the "Content-Type" header). This version has also introduced the concept of name-based virtual hosts. Building on the success of HTTP/1.0, version 1.1 of the protocol adds persistent connections (also referred to as "connection keep-alive") and improved proxy control.
Bulk transfer Both requests and responses might have an associated data blob, also called an entity in HTTP terminology. The size of the entity is determined using one of three different methods: The complete size of the entity is sent as a header (the Content-Length header). The transport layer connection is terminated when transfer of the blob is completed (used by HTTP/0.9 and might be used in HTTP/1.1 in non-persistent mode). Instead of specifying the complete length, smaller chunks of the complete blob are transferred, and each chunk is prefixed with the size of that specific chunk. The end of the stream is denoted by a zero-length chunk. This mode is also called chunked encoding and is specified by the Transfer-Encoding header. Example HTTP transaction GET /index.html HTTP/1.1 Host: www.example.com Connection: keep-alive User-Agent: My-Browser-Type 6.0 HTTP/1.1 200 OK Connection: close Content-Length: 14 <html> </html>
Proxy behavior The default low-level proxy implementation (AbstractHttpProxy) denies all requests by default. Different requests and/or responses can be enabled by using one of the several predefined proxy classes which are suitable for most tasks. Alternatively, a custom proxy class can be derived from AbstractHttpProxy and the requests and responses enabled individually using different attributes. Several examples and considerations on how to enable virus filtering in the HTTP traffic are discussed in the Technical White Paper and Tutorial Virus filtering in HTTP, available at the BalaBit Documentation Page http://www.balabit.com/support/documentation/.
Transparent and non-transparent modes HttpProxy is able to operate both in transparent and non-transparent mode. In transparent mode, the client does not notice (or even know) that it is communicating through a proxy. The client communicates using normal server-style requests. In non-transparent mode, the address and the port of the proxy server must be set on the client. In this case the client sends proxy-style requests to the proxy. Proxy style HTTP query GET http://www.example.com/index.html HTTP/1.1 Host: www.example.com Connection: keep-alive User-Agent: My-Browser-Type 6.0 HTTP/1.1 200 OK Connection: close Content-Length: 14 <html> </html> In non-transparent mode it is possible to request the use of the SSL protocol through the proxy, which means the client communicates with the proxy using the HTTP protocol, but the proxy uses HTTPS to communicate with the server. This technique is called data tunneling. Data tunneling with connect method CONNECT www.example.com:443 HTTP/1.1 Host: www.example.com User-agent: My-Browser-Type 6.0 HTTP/1.0 200 Connection established Proxy-agent: My-Proxy/1.1
Configuring policies for HTTP requests and responses Changing the default behavior of requests is possible using the request attribute. This hash is indexed by the HTTP method names (e.g.: GET or POST). The response attribute (indexed by the request method and the response code) enables the control of HTTP responses. The possible actions are described in the following tables. See also . When looking up entries of the response attribute hash, the lookup precedence described in is used. Implementing URL filtering in the HTTP proxy This example calls the filterURL function (defined in the example) whenever a HTTP GET request is received. If the requested URL is 'http://www.disallowedsite.com', the request is rejected and an error message is sent to the client. class DmzHTTP(HttpProxy): def config(self): HttpProxy.config(self) self.request["GET"] = (HTTP_REQ_POLICY, self.filterURL) def filterURL(self, method, url, version): if (url == "http://www.disallowedsite.com"): self.error_info = 'Access of this content is denied by the local policy.' return HTTP_REQ_REJECT return HTTP_REQ_ACCECT 404 response filtering in HTTP In this example the 404 response code to GET requests is rejected, and a custom error message is returned to the clients instead. class DmzHTTP(HttpProxy): def config(self): HttpProxy.config(self) self.response["GET", "404"] = (HTTP_RSP_POLICY, self.filter404) def filter404(self, method, url, version, response): self.error_status = 404 self.error_info = "Requested page was not accessible." return HTTP_RSP_REJECT
Configuring policies for HTTP headers Both request and response headers can be modified by the proxy during the transfer. New header lines can be inserted, entries can be modified or deleted. To change headers in the requests and responses use the request_header hash or the response_header hash, respectively. Similarly to the request hash, these hashes are indexed by the header name (like "User-Agent") and contain an actiontuple describing the action to take. By default, the proxy modifies only the "Host", "Connection", "Proxy-Connection" and "Transfer-Encoding" headers. "Host" headers need to be changed when the proxy modifies the URL; "(Proxy-)Connection" is changed when the proxy turns connection keep-alive on/off; "Transfer-Enconding" is changed to enable chunked encoding. Header filtering in HTTP The following example hides the browser used by the client by replacing the value of the User-Agent header to Lynx in all requests. The use of cookies is disabled as well. class MyHttp(HttpProxy): def config(self): HttpProxy.config(self) self.request_header["User-Agent"] = (HTTP_HDR_CHANGE_VALUE, "Lynx 2.4.1") self.request_header["Cookie"] = (HTTP_HDR_POLICY, self.processCookies) self.response_header["Set-Cookie"] = (HTTP_HDR_DROP,) def processCookies(self, name, value): # You could change the current header in self.current_header_name # or self.current_header_value, the current request url is # in self.request_url return HTTP_HDR_DROP
Redirecting URLs URLs or sets of URLs can be easily rejected or redirected to a local mirror by modifying some attributes during request processing. When an HTTP request is received, normative policy chains are processed (self.request, self.request_header). Policy callbacks for certain events can be configured with the HTTP_REQ_POLICY or HTTP_HDR_POLICY directives. Any of these callbacks may change the request_url attribute, instructing the proxy to fetch a page different from the one specified by the browser. Please note that this is transparent to the user and does not change the URL in the browser. URL redirection in HTTP proxy This example redirects all HTTP GET requests to the 'http://www.balabit.com/' URL by modifying the value of the requested URL. class MyHttp(HttpProxy): def config(self): HttpProxy.config(self) self.request["GET"] = (HTTP_REQ_POLICY, self.filterURL) def filterURL(self, method, url, version): self.request_url = "http://www.balabit.com/" return HTTP_REQ_ACCEPT Redirecting HTTP to HTTPS This example redirects all incoming HTTP connections to an HTTPS URL. class HttpProxyHttpsredirect(HttpProxy): def config(self): HttpProxy.config(self) self.error_silent = TRUE self.request["GET"] = (HTTP_REQ_POLICY, self.reqRedirect) def reqRedirect(self, method, url, version): self.error_status = 301 #self.error_info = 'HTTP/1.0 301 Moved Permanently' self.error_headers="Location: https://%s/" % self.request_url_host return HTTP_REQ_REJECT
Request types Zorp differentiates between two request types: server requests and proxy request. Server requests are sent by browsers directly communicating with HTTP servers. These requests include an URL relative to the server root (e.g.: /index.html), and a 'Host' header indicating which virtual server to use. Proxy requests are used when the browser communicates with an HTTP proxy. These requests include a fully specified URL (e.g.: http://www.example.com/index.html). As there is no clear distinction between the two request types, the type of the request cannot always be accurately detected automatically, though all common cases are covered. Requests are handled differently in transparent and non-transparent modes. A transparent HTTP proxy (transparent_mode attribute is TRUE) is meant to be installed in front of a network where clients do not know about the presence of the firewall. In this case the proxy expects to see server type requests only. If clients communicate with a real HTTP proxy through the firewall, proxy type requests must be explicitly enabled using the permit_proxy_requests attribute, or transparent mode has to be used. The use of non-transparent HTTP proxies (transparent_mode attribute is FALSE) must be configured in web browsers behind the firewall. In this case Zorp expects proxy requests only, and emits server requests (assuming parent_proxy is not set).
Using parent proxies Parent proxies are non-transparent HTTP proxies used behind Zorp. Two things have to be set in order to use parent proxies. First, select a router which makes the proxy connect to the parent proxy, this can be either InbandRouter() or DirectedRouter(). Second, set the parent_proxy and parent_proxy_port attributes in the HttpProxy class. Setting these attributes results in proxy requests to be emitted to the target server both in transparent and non-transparent mode. The parent proxy attributes can be set both in the configuration phase (e.g.: config() event), or later on a per-request basis. This is possible because the proxy re-connects. Using parent proxies in HTTP In this example the MyHttp proxy class uses a parent proxy. For this the domain name and address of the parent proxy is specified, and a service using an InbandRouter is created. class MyHttp(HttpProxy): def config(self): HttpProxy.config(self) self.parent_proxy = "proxy.example.com" self.parent_proxy_port = 3128 def instance(): Service("http", MyHttp, router=InbandRouter()) Listener(SockAddrInet('10.0.0.1', 80), "http")
FTP over HTTP In non-transparent mode it is possible to let Zorp process ftp:// URLs, effectively translating HTTP requests to FTP requests on the fly. This behaviour can be enabled by setting permit_ftp_over_http to TRUE and adding port 21 to target_port_range. Zorp currently supports passive mode transfers only.
Error messages There are cases when the HTTP proxy must return an error page to the client to indicate certain error conditions. These error messages are stored as files in the directory specified by the error_files_directory attribute, and can be customized by changing the contents of the files in this directory. Each file contains plain HTML text, but some special macros are provided to dynamically add information to the error page. The following macros can be used: @INFO@ -- further error information as provided by the proxy @VERSION@ -- Zorp version number @DATE@ -- current date @HOST@ -- hostname of Zorp It is generally recommended not to display error messages to untrusted clients, as they may leak confidential information. To turn error messages off, set the error_silent attribute to TRUE, or strip error files down to a minimum. The language of the messages can be set using the config.options.language global option, or individually for every Http proxy using the language parameter. See for details.
Stacking HTTP supports stacking proxies for both request and response entities (e.g.: data bodies). This is controlled by the request_stack and response_stack attribute hashes. See also . There are two stacking modes available: HTTP_STK_DATA sends only the data portion to the downstream proxy, while HTTP_STK_MIME also sends all header information to make it possible to process the data body as a MIME envelope. Please note that while it is possible to change the data part in the stacked proxy, it is not possible to change the MIME headers - they can be modified only by the HTTP proxy. The possible parameters are listed in the following tables. Please note that stacking is skipped altogether if there is no body in the message.
Webservers returning data in 205 responses Certain webserver applications may return data entities in 205 responses. This is explicitly prohibited by the RFCs, but Zorp permits such responses for interoperability reasons.
URL filtering in HTTP Starting with version 3.3FR1, Zorp supports category-based URL filtering using a regularly updated database. To configure URL-filtering, see . For the list of categories available by default, see . To customize or expand the URL-database, see .
Configuring URL-filtering in HTTP The URLs and domains in the database are organized into thematic categories like adult, news, jobsearch, etc. To enable url-filtering, set the enable_url_filter and enable_url_filter_dns options to TRUE. The enable_url_filter_dns option is needed only to ensure that a domain or URL is correctly categorized even when it is listed in the database using its domain name, but the client tries to access it with its IP address (or vice-versa). URL-filtering is handled by the Zorp Http proxy, without the need of using ZCV. The URL-filtering capability of Zorp is available only after purchasing the url-filter license option. Updates to the URL database are automatically downloaded daily from the BalaBit website using the zavupdate utility. Access to specific categories can be set using the url_category option, which is a hash indexed by the name of the category. The following actions are possible: URL-filtering example The following example blocks several categories and accepts the rest. For a complete list of categories, see . class MyHTTPUrlFilter(HttpProxy): def config(self): HttpProxy.config(self) self.enable_url_filter=TRUE self.enable_url_filter_dns=TRUE self.url_category['adult']=(HTTP_URL_REJECT, (403, "Adult website",)) self.url_category['porn']=(HTTP_URL_REJECT, (403, "Porn website",)) self.url_category['malware']=(HTTP_URL_REJECT, (403, "Site contains malware",)) self.url_category['phishing']=(HTTP_URL_REJECT, (403, "Phishing site",)) self.url_category['warez']=(HTTP_URL_REJECT, (403, "Warez site",)) self.url_category['*']=(HTTP_URL_ACCEPT,) The following example redirects access to online gaming sites to a dummy website. class MyHTTPUrlFilter(HttpProxy): def config(self): HttpProxy.config(self) self.enable_url_filter=TRUE self.enable_url_filter_dns=TRUE self.url_category['onlinegames']=(HTTP_URL_REDIRECT, "http://example.com") self.url_category['*']=(HTTP_URL_ACCEPT,)
List of URL-filtering categories The Zorp URL database contains the following thematic categories by default. abortion: Abortion information excluding when related to religion ads: Advert servers and banned URLs adult: Sites containing adult material such as swearing but not porn aggressive: Similar to violence but more promoting than depicting antispyware: Sites that remove spyware artnudes: Art sites containing artistic nudity astrology: Astrology websites audio-video: Sites with audio or video downloads banking: Banking websites beerliquorinfo: Sites with information only on beer or liquors beerliquorsale: Sites with beer or liquors for sale blog: Journal/Diary websites cellphones: stuff for mobile/cell phones chat: Sites with chat rooms etc childcare: Sites to do with childcare cleaning: Sites to do with cleaning clothing: Sites about and selling clothing contraception: Information about contraception culinary: Sites about cooking et al dating: Sites about dating desktopsillies: Sites containing screen savers, backgrounds, cursers, pointers, desktop themes and similar timewasting and potentially dangerous content dialers: Sites with dialers such as those for pornography or trojans drugs: Drug related sites ecommerce: Sites that provide online shopping entertainment: Sites that promote movies, books, magazine, humor filehosting: Sites to do with filehosting frencheducation: Sites to do with french education gambling: Gambling sites including stocks and shares games: Game related sites gardening: Gardening sites government: Military and schools etc guns: Sites with guns hacking: Hacking/cracking information homerepair: Sites about home repair hygiene: Sites about hygiene and other personal grooming related stuff instantmessaging: Sites that contain messenger client download and web-based messaging sites jewelry: Sites about and selling jewelry jobsearch: Sites for finding jobs kidstimewasting: Sites kids often waste time on mail: Webmail and email sites marketingware: Sites about marketing products medical: Medical websites mixed_adult: Mixed adult content sites mobile-phone: Sites to do with mobile phones naturism: Sites that contain nude pictures and/or promote a nude lifestyle news: News sites onlineauctions: Online auctions onlinegames: Online gaming sites onlinepayment: Online payment sites personalfinance: Personal finance sites pets: Pet sites phishing: Sites attempting to trick people into giving out private information porn: Pornography proxy: Sites with proxies to bypass filters radio: non-news related radio and television religion: Sites promoting religion ringtones: Sites containing ring tones, games, pictures and other searchengines: Search engines such as google sect: Sites about religious groups sexuality: Sites dedicated to sexuality, possibly including adult material shopping: Shopping sites socialnetworking: Social networking websites sportnews: Sport news sites sports: All sport sites spyware: Sites who run or have spyware software to download updatesites: Sites where software updates are downloaded from including virus sigs vacation: Sites about going on holiday violence: Sites containing violence virusinfected: Sites who host virus infected files warez: Sites with illegal pirate software weather: Weather news sites and weather related weapons: Sites detailing or selling weapons webmail: Just webmail sites whitelist: Contains site suitable for kids
Customizing the URL database To customize the database, you have to manually edit the relevant files of the database. The URL database is located on the Zorp hosts under the /etc/zorp/urlfilter/ directory. Every thematic category is subdirectory containing two files called domains and urls. These files contain the list of domains (e.g., example.com) and URLs (e.g., example.com/news/) that fall into the specific category. Optionally, the subdirectory may contain a third file called expressions, where more complex rules can be defined using regular expressions. To to allow access (whitelist) to a domain or URL, add it to the domains or urls file of the whitelist category. Do not forget to configure your Http proxies to permit access to the domains of the whitelist category. Deleting a domain from a category is not equivalent to whitelisting. Deleted domains will be re-added to their original category after the next database update. To add a new URL or domain to an existing category, create a new subdirectory under /etc/zorp/urlfilter/, create the domains and urls files for this new category, and add the domain or URL (without the http://www. prefix) to the domains or urlsfile. Zorp will automatically add these sites to the specific category after the next daily database update, or when the zufupdate command is executed. To create a new category, create a new subdirectory under /etc/zorp/urlfilter/, create the domains and urls files for this new category, and add domains and URLs to these files. Do not forget to configure your Http proxies to actually use the new category. Manual changes to the URL database are not applied automatically, they become effective only after the next daily database update, or when the zufupdate command is executed. Manual changes are automatically merged with the original database during database updates. If you are using the URL-filter database on several Zorp hosts and modify the database manually, make sure to copy your changes to the other hosts as well.
Related standards The Hypertext Transfer Protocol -- HTTP/1.1 protocol is described in RFC 2616. The Hypertext Transfer Protocol -- HTTP/1.0 protocol is described in RFC 1945.
HTTP request actions These values specify the action to take as a given request arrives. They are used as the first value in the tuple HTTP_REQ_ACCEPT HTTP_REQ_DENY HTTP_REQ_REJECT HTTP_REQ_ABORT HTTP_REQ_POLICY HTTP_RSP_ACCEPT HTTP_RSP_DENY HTTP_RSP_REJECT HTTP_RSP_ABORT HTTP_RSP_POLICY HTTP_HDR_ACCEPT HTTP_HDR_ABORT HTTP_HDR_DROP HTTP_HDR_POLICY HTTP_HDR_CHANGE_NAME HTTP_HDR_CHANGE_VALUE HTTP_HDR_CHANGE_BOTH HTTP_HDR_CHANGE_REGEXP HTTP_HDR_INSERT HTTP_HDR_REPLACE HTTP_CONNECTION_CLOSE HTTP_CONNECTION_KEEPALIVE HTTP_STK_NONE HTTP_STK_DATA HTTP_STK_MIME HTTP_URL_ACCEPT HTTP_URL_REJECT HTTP_URL_REDIRECT Action codes for HTTP requests Allow the request to pass. Reject the request. The reason for the rejection can be specified in the optional second argument. Terminate the connection. Call the function specified to make a decision about the event. The function receives four arguments: self, method, url, version. See for details. Action codes for HTTP responses Allow the response to pass. Reject the response and return a policy violation page to the client. Reject the response and return a policy violation page to the client, with error information optionally specified as the second argument. Call the function specified to make a decision about the event. The function receives five parameters: self, method, url, version, response. See for details. Action codes for HTTP headers Terminate the connection. Accept the header. Remove the header. Call the function specified to make a decision about the event. The function receives three parameters: self, hdr_name, and hdr_value. Rename the header to the name specified in the second argument. Change the value of the header to the value specified in the second argument. Change both the name and value of the header to the values specified in the second and third arguments, respectively. Insert a new header defined in the second argument. Remove all existing occurrences of a header and replace them with the one specified in the second argument. Constants for proxy stacking No additional proxy is stacked into the HTTP proxy. The data part of the HTTP traffic is passed to the specified stacked proxy. The data part including header information of the HTTP traffic is passed to the specified stacked proxy. Action codes for URL filtering Permit access to the URL. Reject the request. The error code and reason for the rejection can be specified in the optional second and third arguments. See for details. Redirect the connection to the URL specified in the second argument. HTTP_DEBUG"http.debug" HTTP_ERROR"http.error" HTTP_POLICY"http.policy" HTTP_REQUEST"http.request" HTTP_RESPONSE"http.response" HTTP_VIOLATION"http.violation" HTTP_ACCOUNTING"http.accounting"
""" from Zorp import * from Plug import PlugProxy from Proxy import Proxy, proxyLog from Session import StackedSession from Matcher import getMatcher HTTP_URL_ACCEPT = 1 HTTP_URL_REJECT = 3 HTTP_URL_REDIRECT = 106 HTTP_REQ_ACCEPT = 1 HTTP_REQ_DENY = 2 HTTP_REQ_REJECT = 3 HTTP_REQ_ABORT = 4 HTTP_REQ_POLICY = 6 HTTP_RSP_ACCEPT = 1 HTTP_RSP_DENY = 2 HTTP_RSP_REJECT = 3 HTTP_RSP_ABORT = 4 HTTP_RSP_POLICY = 6 HTTP_HDR_ACCEPT = 1 HTTP_HDR_ABORT = 4 HTTP_HDR_DROP = 5 HTTP_HDR_POLICY = 6 HTTP_HDR_CHANGE_NAME = 100 HTTP_HDR_CHANGE_VALUE = 101 HTTP_HDR_CHANGE_BOTH = 102 HTTP_HDR_CHANGE_REGEXP = 103 HTTP_HDR_INSERT = 104 HTTP_HDR_REPLACE = 105 HTTP_CONNECTION_CLOSE = 0 HTTP_CONNECTION_KEEPALIVE = 1 HTTP_DEBUG = "http.debug" HTTP_ERROR = "http.error" HTTP_POLICY = "http.policy" HTTP_REQUEST = "http.request" HTTP_RESPONSE = "http.response" HTTP_VIOLATION = "http.violation" HTTP_ACCOUNTING = "http.accounting" HTTP_STK_NONE = 1 HTTP_STK_DATA = 2 HTTP_STK_MIME = 3 HTTP_STK_POLICY = 4 class AbstractHttpProxy(Proxy): """ Class encapsulating the abstract HTTP proxy. This class implements an abstract HTTP proxy - it serves as a starting point for customized proxy classes, but is itself not directly usable. Service definitions should refer to a customized class derived from AbstractHttpProxy, or one of the predefined proxy classes, such as HttpProxy or HttpProxyNonTransparent. AbstractHttpProxy denies all requests by default. transparent_mode TRUE Set the operation mode of the proxy to transparent (TRUE) or non-transparent (FALSE). permit_server_requests TRUE Allow server-type requests in non-transparent mode. permit_proxy_requests FALSE Allow proxy-type requests in transparent mode. permit_ftp_over_http FALSE Allow processing FTP URLs in non-transparent mode. permit_unicode_url FALSE Allow unicode characters in URLs encoded as %u. This is an IIS extension to HTTP, UNICODE (UTF-7, UTF-8 etc.) URLs are forbidden by the RFC as default. permit_invalid_hex_escape FALSE Allow invalid hexadecimal escaping in URLs (% must be followed by two hexadecimal digits). permit_http09_responses TRUE Allow server responses to use the limited HTTP/0.9 protocol. As these responses carry no control information, verifying the validity of the protocol stream is impossible. This does not pose a threat to web clients, but exploits might pass undetected if this option is enabled for servers. It is recommended to turn this option off for protecting servers and only enable it when Zorp is used in front of users. permit_both_connection_headers FALSE Some clients send both a Connection and a Proxy-Connection header, which are used by Zorp to automatically detect what kind of connection Zorp receives. This situation is forbidden in Zorp by default but can be enabled by setting this attribute to TRUE. keep_persistent FALSE Try to keep the connection to the client persistent even if the server does not support it. connection_mode This value reflects the state of the session. If the value equals to 'HTTP_CONNECTION_CLOSE', the session will be closed after serving the current request. Otherwise, if the value is 'HTTP_CONNECTION_KEEPALIVE' another request will be fetched from the client. This attribute can be used to forcibly close a keep-alive connection. parent_proxy "" The address or hostname of the parent proxy to be connected. Either DirectedRouter or InbandRouter has to be used when using parent proxy. parent_proxy_port 3128 The port of the parent proxy to be connected. default_port 80 This value is used in non-transparent mode when the requested URL does not contain a port number. The default should be 80, otherwise the proxy may not function properly. use_default_port_in_transparent_mode TRUE Set the target port to the value of default_port in transparent mode. This ensures that only the ports specified in target_port_range can be used by the clients, even if InbandRouter is used. use_canonicalized_urls TRUE This attribute enables URL canonicalization, which means to automatically convert URLs to their canonical form. This enhances security but might cause interoperability problems with some applications. It is recommended to disable this setting on a per-destination basis. URL filtering still sees the canonicalized URL, but at the end the proxy sends the original URL to the server. rewrite_host_header TRUE Rewrite the Host header in requests when URL redirection is performed. reset_on_close FALSE Whenever the connection is terminated without a proxy generated error message, send an RST instead of a normal close. Causes some clients to automatically reconnect. require_host_header TRUE Require the presence of the Host header. If set to FALSE, the real URL cannot be recovered from certain requests, which might cause problems with URL filtering. strict_header_checking FALSE Require RFC conformant HTTP headers. strict_header_checking_action HTTP_HDR_DROP This attribute control what will the Zorp do if a non-rfc conform or unknown header found in the communication. Only the HTTP_HDR_ACCEPT, HTTP_HDR_DROP and HTTP_HDR_ABORT can be used. permit_null_response TRUE Permit RFC incompliant responses with headers not terminated by CRLF and not containing entity body. max_hostname_length 256 Maximum allowed length of the hostname field in URLs. max_line_length 4096 Maximum allowed length of lines in requests and responses. This value does not affect data transfer, as data is transmitted in binary mode. max_url_length 4096 Maximum allowed length of an URL in a request. Note that this directly affects forms using the 'GET' method to pass data to CGI scripts. max_body_length 0 Maximum allowed length of an HTTP request or response body. The default "0" value means that the length of the body is not limited. max_chunk_length 0 Maximum allowed length of a single chunk when using chunked transfer-encoding. The default "0" value means that the length of the chunk is not limited. max_header_lines 50 Maximum number of header lines allowed in a request or response. max_keepalive_requests 0 Maximum number of requests allowed in a single session. If the number of requests in the session the reaches this limit, the connection is terminated. The default "0" value allows unlimited number of requests. request_count 0 The number of keepalive requests within the session. timeout 300000 General I/O timeout in milliseconds. If there is no timeout specified for a given operation, this value is used. timeout_request 10000 Time to wait for a request to arrive from the client. timeout_response 300000 Time to wait for the HTTP status line to arrive from the server. rerequest_attempts 0 Controls the number of attempts the proxy takes to send the request to the server. In case of server failure, a reconnection is made and the complete request is repeated along with POST data. buffer_size 1500 Size of the I/O buffer used to transfer entity bodies. request empty Normative policy hash for HTTP requests indexed by the HTTP method (e.g.: "GET", "PUT" etc.). See also . request_header empty Normative policy hash for HTTP header requests indexed by the header names (e.g.: "Set-cookie"). See also . response empty Normative policy hash for HTTP responses indexed by the HTTP method and the response code (e.g.: "PWD", "209" etc.). See also . response_header empty Normative policy hash for HTTP header responses indexed by the header names (e.g.: "Set-cookie"). See also . response_mime_type n/a The MIME type of the response entity. Its value is only defined when the response is processed. request_method n/a Request method (GET, POST, etc.) sent by the client. request_url_scheme n/a Protocol specifier of the URL (http://, ftp://, etc.). request_url n/a The URL requested by the client. It can be modified to redirect the current request. request_url_proto n/a Protocol specifier of the URL. This attribute is an alias for request_url_scheme. request_url_username n/a Username in the URL (if specified). request_url_passwd n/a Password in the URL (if specified). request_url_host n/a Remote hostname in the URL. request_url_port n/a Port number as specified in the URL. request_url_file n/a Filename specified in the URL. request_mime_type n/a The MIME type of the request entity. Its value is only defined when the request is processed. current_header_name n/a Name of the header. It is defined when the header is processed, and can be modified by the proxy to actually change a header in the request or response. current_header_value n/a Value of the header. It is defined when the header is processed, and can be modified by the proxy to actually change the value of the header in the request or response. error_status 500 If an error occurs, Zorp uses this value as the status code of the HTTP response it generates. error_info n/a A string to be included in error messages. error_msg n/a A string used as an error message in the HTTP status line. error_headers n/a A string included as a header in the error response. The string must be a valid header and must end with a "\r\n" sequence. error_silent FALSE Turns off verbose error reporting to the HTTP client (makes firewall fingerprinting more difficult). error_files_directory "/usr/share/zorp/http" Location of HTTP error messages. auth_forward FALSE Controls whether inband authentication information (username and password) should be forwarded to the upstream server. When a parent proxy is present, the incoming authentication request is put into a 'Proxy-Authorization' header. In other cases the 'WWW-Authorization' header is used. auth_inband_supported 1 auth n/a auth_realm "Zorp HTTP auth" The name of the authentication realm to be presented to the user in the dialog window during inband authentication. target_port_range "80,443" List of ports that non-transparent requests are allowed to use. The default is to allow port 80 and 443 to permit HTTP and HTTPS traffic. (The latter also requires the CONNECT method to be enabled). request_stack Attribute containing the request stacking policy: the hash is indexed based on method names (e.g.: GET). See . response_stack Attribute containing the response stacking policy: the hash is indexed based on method names (e.g.: GET). See . connect_proxy PlugProxy For CONNECT requests the HTTP proxy starts an independent proxy to control the internal protocol. The connect_proxy attribute specifies which proxy class is used for this purpose. max_auth_time 0 Request password authentication from the client, invalidating cached one-time-passwords. If the time specified (in seconds) in this attribute expires, Zorp requests a new authentication from the client browser even if it still has a password cached. auth_by_cookie FALSE Authentication informations for one-time-password mode is organized by a cookie not the address of the client. auth_cache_time 0 Caching authentication information this amount of seconds. auth_cache_update FALSE Update authentication cache by every connection. enable_url_filter FALSE Enables URL filtering in HTTP requests. See for details. Note that URL filtering requires the url-filter license option. enable_url_filter_dns FALSE Enables DNS- and reverse-DNS resolution to ensure that a domain or URL is correctly categorized even when it is listed in the database using its domain name, but the client tries to access it with its IP address (or vice-versa). See for details. Note that URL filtering requires the url-filter license option. url_category empty Normative policy hash for category-based URL-filtering. The hash is indexed by the name of the category. See also . transparent_server_requests A deprecated alias of the permit_server_requests attribute. transparent_proxy_requests A deprecated alias of permit_proxy_requests. request_timeout A deprecated alias of timeout_request. request_headers A deprecated alias of request_header. response_headers A deprecated alias of response_header. error_response A deprecated alias of error_status. """ name = "http" auth_inband_supported = TRUE def __init__(self, session): """ Initializes a HttpProxy instance. session SESSION instance the session this instance participates in Creates and initializes a HttpProxy instance. """ self.connect_proxy = PlugProxy self.request_stack = {} self.response_stack = {} Proxy.__init__(self, session) def requestStack(self, side): """ """ if side == 0: hash = self.request_stack else: hash = self.response_stack self.transfer_from = side try: stack_proxy = hash[self.request_method] except KeyError: try: stack_proxy = hash["*"] except KeyError: return (HTTP_STK_NONE, None) if type(stack_proxy) == type(()): while 1: stack_type = stack_proxy[0] if stack_type == HTTP_STK_NONE: return (HTTP_STK_NONE, None) elif stack_type == HTTP_STK_POLICY: # call function stack_proxy = stack_proxy[1](side) else: return stack_proxy else: return (HTTP_STK_NONE, None) def connectMethod(self): """ Create a connect_proxy instance. """ return self.stackProxy(self.session.client_stream, self.session.server_stream, self.connect_proxy, None) def getRequestHeader(self, header): """ Function returning the value of a request header. header STRING Name of the header to look up. This function looks up and returns the value of a header associated with the current request. """ return self.__headerManip(0, 0, header) def setRequestHeader(self, header, new_value): """ Function changing the value of a request header. header STRING Name of the header to change. new_value STRING Change the header to this value. This function looks up and changes the value of a header associated with the current request. """ return self.__headerManip(1, 0, header, new_value) def getResponseHeader(self, header): """ Function returning the value of a response header. header STRING Name of the header to look up. This function looks up and returns the value of a header associated with the current response. """ return self.__headerManip(0, 1, header) def setResponseHeader(self, header, new_value): """ Function changing the value of a response header. header STRING Name of the header to change. new_value STRING Change the header to this value. This function looks up and changes the value of a header associated with the current response. """ return self.__headerManip(1, 1, header, new_value) class HttpProxy(AbstractHttpProxy): """ Default HTTP proxy based on AbstractHttpProxy. HttpProxy is a default HTTP proxy based on AbstractHttpProxy. It is transparent, and enables the most commonly used HTTP methods: "GET", "POST" and "HEAD". """ def config(self): """ Default config event handler. Enables the most common HTTP methods so we have a useful default configuration. """ self.request["GET"] = (HTTP_REQ_ACCEPT,) self.request["POST"] = (HTTP_REQ_ACCEPT,) self.request["HEAD"] = (HTTP_REQ_ACCEPT,) # we are transparent by default HttpProxyTransparent = HttpProxy; class HttpProxyNonTransparent(HttpProxy): """ HTTP proxy based on HttpProxy, operating in non-transparent mode. HTTP proxy based on HttpProxy. This class is identical to HttpProxy with the only difference being that it is non-transparent (transparent_mode = FALSE). Consequently, clients must be explicitly configured to connect to this proxy instead of the target server and issue proxy requests. On the server side this proxy connects transparently to the target server. For the correct operation the proxy must be able to set the server address on its own. This can be accomplished by using InbandRouter. """ def config(self): """ Config event handler Sets self.transparent_mode to FALSE to indicate non-transparent mode. """ HttpProxy.config(self) self.transparent_mode = FALSE class HttpProxyURIFilter(HttpProxy): """ HTTP proxy based on HttpProxy, with URI filtering capability. HTTP proxy based on HttpProxy, having URL filtering capability. The matcher attribute should be initialized to refer to a Matcher object. The initialization should be done in the class body as shown in the next example. URL filtering HTTP proxy class MyHttp(HttpProxyURIFilter): matcher = RegexpFileMatcher('/etc/zorp/blacklist.txt', \ '/etc/zorp/whitelist.txt') matcher None Matcher determining whether access to an URL is permitted or not. """ def config(self): """ """ HttpProxy.config(self) self.request["GET"] = (HTTP_REQ_POLICY, self.checkURL) self.request["POST"] = (HTTP_REQ_POLICY, self.checkURL) self.request["HEAD"] = (HTTP_REQ_POLICY, self.checkURL) def __post_config__(self): """ """ HttpProxy.__post_config__(self) if not hasattr(self, "matcher"): self.matcher = None else: self.matcher = getMatcher(self.matcher) def checkURL(self, method, url, version): """ """ ## LOG ## # This is an accounting message that reports request details. ## proxyLog(self, HTTP_ACCOUNTING, 4, "Http accounting; request='%s %s %s'" % (method, url, version)) if self.matcher: if self.matcher.checkMatch(url): ## LOG ## # This message indicates that the request was blocked by the URIFilter. ## proxyLog(self, HTTP_REQUEST, 6, "Request administratively prohibited; request='%s %s %s'" % (method, url, version)) self.error_info = 'Accessing this content was administratively prohibited.' return HTTP_REQ_REJECT return HTTP_REQ_ACCEPT class HttpProxyURIFilterNonTransparent(HttpProxyURIFilter): """ HTTP proxy based on HttpProxyURIFilter, with URI filtering capability and permitting non-transparent requests. HTTP proxy based on HttpProxyURIFilter, but operating in non-transparent mode (transparent_mode = FALSE). """ def config(self): """ """ HttpProxyURIFilter.config(self) self.transparent_mode = FALSE class HttpWebdavProxy(HttpProxy): """ HTTP proxy based on HttpProxy, allowing WebDAV extensions. HTTP proxy based on HttpProxy, also capable of inspecting WebDAV extensions of the HTTP protocol. The following requests are permitted: PROPFIND; PROPPATCH; MKCOL; COPY; MOVE; LOCK; UNLOCK. """ def config(self): """ """ HttpProxy.config(self) self.request["PROPFIND"] = (HTTP_REQ_ACCEPT) self.request["PROPPATCH"] = (HTTP_REQ_ACCEPT) self.request["MKCOL"] = (HTTP_REQ_ACCEPT) self.request["COPY"] = (HTTP_REQ_ACCEPT) self.request["MOVE"] = (HTTP_REQ_ACCEPT) self.request["LOCK"] = (HTTP_REQ_ACCEPT) self.request["UNLOCK"] = (HTTP_REQ_ACCEPT) class NontransHttpWebdavProxy(HttpProxyNonTransparent): """ HTTP proxy based on HttpProxyNonTransparent, allowing WebDAV extension in non-transparent requests. HTTP proxy based on HttpProxyNonTransparent, operating in non-transparent mode (transparent_mode = FALSE) and capable of inspecting WebDAV extensions of the HTTP protocol. The following requests are permitted: PROPFIND; PROPPATCH; MKCOL; COPY; MOVE; LOCK; UNLOCK. """ def config(self): """ """ HttpProxyNonTransparent.config(self) self.request["PROPFIND"] = (HTTP_REQ_ACCEPT) self.request["PROPPATCH"] = (HTTP_REQ_ACCEPT) self.request["MKCOL"] = (HTTP_REQ_ACCEPT) self.request["COPY"] = (HTTP_REQ_ACCEPT) self.request["MOVE"] = (HTTP_REQ_ACCEPT) self.request["LOCK"] = (HTTP_REQ_ACCEPT) self.request["UNLOCK"] = (HTTP_REQ_ACCEPT) zorp-3.9.5/modules/http/Makefile.am000066400000000000000000000005341172670260400172140ustar00rootroot00000000000000SUBDIRS = . messages tests pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ AM_CFLAGS = @CFLAGS@ -W pkgdata_DATA = Http.py pkglib_LTLIBRARIES = libhttp.la libhttp_la_SOURCES = http.c httpproto.c httpfltr.c httpmisc.c httphdr.c httpftp.c http.h EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/http/http.c000066400000000000000000003277211172670260400163150ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * Slightly based on the code by: Viktor Peter Kovacs * ***************************************************************************/ #include "http.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static GHashTable *auth_hash = NULL; static GMutex *auth_mutex = NULL; typedef struct _ZorpAuthInfo { time_t last_auth_time; time_t accept_credit; time_t create_time; } ZorpAuthInfo; /** * http_filter_hash_compare: * @a: first item * @b: second item * * This function is the hash compare function for request header hashes. **/ gint http_filter_hash_compare(gconstpointer a, gconstpointer b) { z_enter(); if (strcasecmp((char *) a, (char *) b) == 0) z_return(1); z_return(0); } /** * http_filter_hash_bucket: * @a: item to calculate hash value for * * This function is the hash calculation function for request header hashes. **/ gint http_filter_hash_bucket(gconstpointer a) { int sum = 0; char *s = (char *) a; z_enter(); while (*s != 0) { sum += toupper(*s); s++; } z_return(sum % 16); } /** * http_config_set_defaults: * @self: HttpProxy instance * * This function initializes various attributes exported to the Python layer * for possible modification. **/ static void http_config_set_defaults(HttpProxy *self) { z_proxy_enter(self); self->connection_mode = HTTP_CONNECTION_CLOSE; self->server_connection_mode = HTTP_CONNECTION_CLOSE; self->force_reconnect = FALSE; self->transparent_mode = TRUE; self->permit_server_requests = TRUE; self->permit_proxy_requests = FALSE; self->permit_unicode_url = FALSE; self->permit_http09_responses = TRUE; self->rewrite_host_header = TRUE; self->require_host_header = TRUE; self->strict_header_checking = FALSE; self->strict_header_checking_action = ZV_DROP; self->permit_null_response = TRUE; self->max_line_length = 4096; self->max_url_length = 4096; self->max_header_lines = 50; self->max_hostname_length = 256; self->max_chunk_length = 0; self->timeout_request = 10000; self->timeout_response = 300000; self->timeout = 300000; self->default_http_port = 80; self->default_ftp_port = 21; self->use_default_port_in_transparent_mode = TRUE; self->use_canonicalized_urls = TRUE; self->max_body_length = 0; self->buffer_size = 1500; self->rerequest_attempts = 0; http_init_headers(&self->headers[EP_CLIENT]); http_init_headers(&self->headers[EP_SERVER]); http_init_url(&self->request_url_parts); self->request_url = g_string_sized_new(128); self->current_header_name = g_string_sized_new(16); self->current_header_value = g_string_sized_new(32); self->parent_proxy = g_string_sized_new(0); self->parent_proxy_port = 3128; self->target_port_range = g_string_new("80,443"); self->auth_header_value = g_string_sized_new(32); self->remote_server = g_string_sized_new(32); self->connected_server = g_string_sized_new(32); self->request_method = g_string_sized_new(16); self->response_msg = g_string_sized_new(32); self->request_method_policy = g_hash_table_new((GHashFunc) http_filter_hash_bucket, (GCompareFunc) http_filter_hash_compare); self->request_header_policy = g_hash_table_new((GHashFunc) http_filter_hash_bucket, (GCompareFunc) http_filter_hash_compare); self->response_policy = z_dim_hash_table_new(1, 2, DIMHASH_WILDCARD, DIMHASH_CONSUME); self->response_header_policy = g_hash_table_new((GHashFunc) http_filter_hash_bucket, (GCompareFunc) http_filter_hash_compare); self->error_info = g_string_sized_new(0); self->error_msg = g_string_sized_new(0); self->error_headers = g_string_sized_new(0); self->error_code = -1; self->error_status = 500; self->error_files_directory = g_string_sized_new(0); self->error_silent = FALSE; self->max_auth_time = 0; self->auth_realm = g_string_new("Zorp HTTP auth"); self->old_auth_header = g_string_sized_new(0); self->auth_by_cookie = FALSE; self->request_categories = NULL; z_proxy_return(self); } /** * http_config_init: * @self: HttpProxy instance * * This function is called right after the config() method to initialize * settings the Python layer specified for us. **/ static void http_config_init(HttpProxy *self) { z_proxy_enter(self); if (self->max_line_length > HTTP_MAX_LINE) self->max_line_length = HTTP_MAX_LINE; self->super.endpoints[EP_CLIENT]->timeout = self->timeout_request; self->poll = z_poll_new(); z_proxy_return(self); } /** * http_query_request_url: * @self: HttpProxy instance * @name: name of requested variable * @value: unused * * This function is registered as a Z_VAR_TYPE_CUSTOM get handler, e.g. it * is called whenever one of the request_url_* attributes are requested from * Python. Instead of presetting those attributes before calling into Python * we calculate their value dynamically. **/ static ZPolicyObj * http_query_request_url(HttpProxy *self, gchar *name, gpointer value G_GNUC_UNUSED) { ZPolicyObj *res = NULL; z_proxy_enter(self); if (strcmp(name, "request_url") == 0) res = z_policy_var_build("s#", self->request_url->str, self->request_url->len); else if (strcmp(name, "request_url_proto") == 0 || strcmp(name, "request_url_scheme") == 0) res = z_policy_var_build("s#", self->request_url_parts.scheme->str, self->request_url_parts.scheme->len); else if (strcmp(name, "request_url_username") == 0) res = z_policy_var_build("s#", self->request_url_parts.user->str, self->request_url_parts.user->len); else if (strcmp(name, "request_url_passwd") == 0) res = z_policy_var_build("s#", self->request_url_parts.passwd->str, self->request_url_parts.passwd->len); else if (strcmp(name, "request_url_host") == 0) res = z_policy_var_build("s#", self->request_url_parts.host->str, self->request_url_parts.host->len); else if (strcmp(name, "request_url_port") == 0) res = z_policy_var_build("i", self->request_url_parts.port ? self->request_url_parts.port : self->default_http_port); else if (strcmp(name, "request_url_file") == 0) res = z_policy_var_build("s#", self->request_url_parts.file->str, self->request_url_parts.file->len); else if (strcmp(name, "request_url_query") == 0) res = z_policy_var_build("s#", self->request_url_parts.query->str, self->request_url_parts.query->len); else PyErr_SetString(PyExc_AttributeError, "Unknown attribute"); z_proxy_return(self, res); } /** * http_set_request_url: * @self: HttpProxy instance * @name: name of requested variable * @value: unused * @new: new value for attribute * * This function is registered as a Z_VAR_TYPE_CUSTOM set handler, e.g. it * is called whenever the request_url attribute are changed from Python. * We need to reparse the URL in these cases. **/ static gint http_set_request_url(HttpProxy *self, gchar *name G_GNUC_UNUSED, gpointer value G_GNUC_UNUSED, PyObject *new) { z_proxy_enter(self); if (strcmp(name, "request_url") == 0) { gchar *str; const gchar *reason; if (!PyArg_Parse(new, "s", &str)) z_proxy_return(self, -1); if (!http_parse_url(&self->request_url_parts, self->permit_unicode_url, self->permit_invalid_hex_escape, FALSE, str, &reason)) { z_proxy_log(self, HTTP_ERROR, 2, "Policy tried to force an invalid URL; url='%s', reason='%s'", str, reason); z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid URL."); z_proxy_return(self, 0); } if (!http_format_url(&self->request_url_parts, self->request_url, TRUE, self->permit_unicode_url, TRUE, &reason)) { z_proxy_log(self, HTTP_ERROR, 2, "Error canonicalizing URL; url='%s', reason='%s'", str, reason); z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid URL."); z_proxy_return(self, -1); } z_proxy_return(self, 0); } z_policy_raise_exception_obj(z_policy_exc_attribute_error, "Can only set request_url"); z_proxy_return(self, -1); } /** * http_query_mime_type: * @self: HttpProxy instance * @name: name of requested variable * @value: unused * * This function is registered as a Z_VAR_TYPE_CUSTOM get handler, e.g. it * is called whenever one of the request_mime_type or response_mime_type * attributes are requested from Python. Instead of presetting those * attributes before calling into Python we calculate their value * dynamically. **/ static ZPolicyObj * http_query_mime_type(HttpProxy *self, gchar *name, gpointer value G_GNUC_UNUSED) { ZPolicyObj *res = NULL; HttpHeader *hdr; gboolean success; z_proxy_enter(self); if (strcmp(name, "request_mime_type") == 0) { success = http_lookup_header(&self->headers[EP_CLIENT], "Content-Type", &hdr); } else if (strcmp(name, "response_mime_type") == 0) { success = http_lookup_header(&self->headers[EP_SERVER], "Content-Type", &hdr); } else { PyErr_SetString(PyExc_AttributeError, "Unknown attribute"); z_proxy_return(self, NULL); } if (!success || !hdr) { res = PyString_FromString(""); } else { gchar *start, *end; start = hdr->value->str; while (*start == ' ') start++; end = strchr(hdr->value->str, ';'); if (end) { end--; while (end > start && *end == ' ') end--; } if (end) res = PyString_FromStringAndSize(hdr->value->str, (end - start + 1)); else res = PyString_FromString(hdr->value->str); } z_proxy_return(self, res); } static ZPolicyObj * http_policy_header_manip(HttpProxy *self, ZPolicyObj *args) { gint action, side; gchar *header, *new_value = NULL; HttpHeader *p = NULL; ZPolicyObj *res = NULL; z_proxy_enter(self); if (!z_policy_var_parse_tuple(args, "iis|s", &action, &side, &header, &new_value)) goto error; side &= 1; switch (action) { case 0: /* get */ if (http_lookup_header(&self->headers[side], header, &p)) { res = z_policy_var_build("s", p->value->str); } else { res = z_policy_none_ref(); } break; case 1: /* set */ if (!new_value) goto error_set_exc; if (!http_lookup_header(&self->headers[side], header, &p)) p = http_add_header(&self->headers[side], header, strlen(header), new_value, strlen(new_value)); g_string_assign(p->value, new_value); p->present = TRUE; res = z_policy_none_ref(); break; default: goto error_set_exc; } z_proxy_return(self, res); error_set_exc: z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid arguments."); error: z_proxy_return(self, NULL); } /** * http_register_vars: * @self: HttpProxy instance * * This function is called upon startup to export Python attributes. **/ static void http_register_vars(HttpProxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "transparent_mode", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->transparent_mode); z_proxy_var_new(&self->super, "permit_server_requests", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_server_requests); z_proxy_var_new(&self->super, "permit_null_response", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_null_response); z_proxy_var_new(&self->super, "permit_proxy_requests", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_proxy_requests); z_proxy_var_new(&self->super, "permit_unicode_url", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_unicode_url); z_proxy_var_new(&self->super, "permit_invalid_hex_escape", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_invalid_hex_escape); z_proxy_var_new(&self->super, "permit_http09_responses", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_http09_responses); z_proxy_var_new(&self->super, "permit_both_connection_headers", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_both_connection_headers); z_proxy_var_new(&self->super, "permit_ftp_over_http", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->permit_ftp_over_http); /* close or keep-alive */ z_proxy_var_new(&self->super, "connection_mode", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET, &self->connection_mode); z_proxy_var_new(&self->super, "keep_persistent", Z_VAR_TYPE_INT | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG | Z_VAR_GET, &self->keep_persistent); /* string containing parent proxy */ z_proxy_var_new(&self->super, "parent_proxy", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, self->parent_proxy); /* parent proxy port */ z_proxy_var_new(&self->super, "parent_proxy_port", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->parent_proxy_port); /* default port if portnumber is not specified in urls */ z_proxy_var_new(&self->super, "default_port", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, "default_http_port"); z_proxy_var_new(&self->super, "use_default_port_in_transparent_mode", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->use_default_port_in_transparent_mode); z_proxy_var_new(&self->super, "use_canonicalized_urls", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->use_canonicalized_urls); z_proxy_var_new(&self->super, "default_http_port", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->default_http_port); z_proxy_var_new(&self->super, "default_ftp_port", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->default_ftp_port); /* rewrite host header when redirecting */ z_proxy_var_new(&self->super, "rewrite_host_header", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->rewrite_host_header); z_proxy_var_new(&self->super, "reset_on_close", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->reset_on_close); /* require host header */ z_proxy_var_new(&self->super, "require_host_header", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->require_host_header); /* enable strict header checking */ z_proxy_var_new(&self->super, "strict_header_checking", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->strict_header_checking); /* enable strict header checking */ z_proxy_var_new(&self->super, "strict_header_checking_action", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->strict_header_checking_action); /* integer */ z_proxy_var_new(&self->super, "max_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_line_length); /* integer */ z_proxy_var_new(&self->super, "max_url_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_url_length); /* integer */ z_proxy_var_new(&self->super, "max_hostname_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_hostname_length); /* integer */ z_proxy_var_new(&self->super, "max_header_lines", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_header_lines); /* integer */ z_proxy_var_new(&self->super, "max_keepalive_requests", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_keepalive_requests); /* integer */ z_proxy_var_new(&self->super, "max_body_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_body_length); /* integer */ z_proxy_var_new(&self->super, "max_chunk_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->max_chunk_length); /* integer */ z_proxy_var_new(&self->super, "request_count", Z_VAR_TYPE_INT | Z_VAR_GET, &self->request_count); /* timeout value in milliseconds */ z_proxy_var_new(&self->super, "timeout", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->timeout); /* timeout value in milliseconds */ z_proxy_var_new(&self->super, "buffer_size", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->buffer_size); /* request timeout value in milliseconds */ z_proxy_var_new(&self->super, "timeout_request", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, &self->timeout_request); /* response timeout value in milliseconds */ z_proxy_var_new(&self->super, "timeout_response", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, &self->timeout_response); /* number of rerequest attempts */ z_proxy_var_new(&self->super, "rerequest_attempts", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, &self->rerequest_attempts); /* hash indexed by request method */ z_proxy_var_new(&self->super, "request", Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->request_method_policy); /* hash indexed by header name */ z_proxy_var_new(&self->super, "request_header", Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->request_header_policy); /* hash indexed by response code */ z_proxy_var_new(&self->super, "response", Z_VAR_TYPE_DIMHASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->response_policy); /* hash indexed by header name */ z_proxy_var_new(&self->super, "response_header", Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->response_header_policy); /* header manipulation */ z_proxy_var_new(&self->super, "_AbstractHttpProxy__headerManip", Z_VAR_TYPE_METHOD | Z_VAR_GET, self, http_policy_header_manip); /* string containing current url proto */ z_proxy_var_new(&self->super, "request_method", Z_VAR_TYPE_STRING | Z_VAR_GET, self->request_method); /* string containing current url */ z_proxy_var_new(&self->super, "request_url", Z_VAR_TYPE_CUSTOM | Z_VAR_GET | Z_VAR_SET, NULL, http_query_request_url, http_set_request_url, NULL); /* string containing current url proto */ z_proxy_var_new(&self->super, "request_url_proto", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url proto */ z_proxy_var_new(&self->super, "request_url_scheme", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url username */ z_proxy_var_new(&self->super, "request_url_username", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url passwd */ z_proxy_var_new(&self->super, "request_url_passwd", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url hostname */ z_proxy_var_new(&self->super, "request_url_host", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url port */ z_proxy_var_new(&self->super, "request_url_port", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url file */ z_proxy_var_new(&self->super, "request_url_file", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url query */ z_proxy_var_new(&self->super, "request_url_query", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing current url fragment */ z_proxy_var_new(&self->super, "request_url_fragment", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_request_url, NULL, NULL); /* string containing request mime type */ z_proxy_var_new(&self->super, "request_mime_type", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_mime_type, NULL, NULL); /* string containing response mime type */ z_proxy_var_new(&self->super, "response_mime_type", Z_VAR_TYPE_CUSTOM | Z_VAR_GET, NULL, http_query_mime_type, NULL, NULL); /* string containing current header name */ z_proxy_var_new(&self->super, "current_header_name", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->current_header_name); /* string containing current header value */ z_proxy_var_new(&self->super, "current_header_value", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->current_header_value); /* error response */ z_proxy_var_new(&self->super, "error_status", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->error_status); /* error silence */ z_proxy_var_new(&self->super, "error_silent", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->error_silent); /* string inserted into error messages */ z_proxy_var_new(&self->super, "error_info", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->error_info); z_proxy_var_new(&self->super, "error_msg", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->error_msg); z_proxy_var_new(&self->super, "error_headers", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->error_headers); z_proxy_var_new(&self->super, "auth_forward", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, &self->auth_forward); z_proxy_var_new(&self->super, "auth", Z_VAR_TYPE_OBJECT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->auth); z_proxy_var_new(&self->super, "auth_realm", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG, self->auth_realm); z_proxy_var_new(&self->super, "max_auth_time", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_auth_time); z_proxy_var_new(&self->super, "target_port_range", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, self->target_port_range); z_proxy_var_new(&self->super, "error_files_directory", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, self->error_files_directory); /* compatibility with Zorp 0.8.x */ z_proxy_var_new(&self->super, "transparent_server_requests", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, "permit_server_requests"); z_proxy_var_new(&self->super, "transparent_proxy_requests", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, "permit_proxy_requests"); z_proxy_var_new(&self->super, "request_timeout", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, "timeout_request"); z_proxy_var_new(&self->super, "request_headers", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_GET_CONFIG, "request_header"); z_proxy_var_new(&self->super, "response_headers", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_GET_CONFIG, "response_header"); z_proxy_var_new(&self->super, "url_proto", Z_VAR_TYPE_ALIAS | Z_VAR_GET, "request_url_proto"); z_proxy_var_new(&self->super, "url_username", Z_VAR_TYPE_ALIAS | Z_VAR_GET, "request_url_username"); z_proxy_var_new(&self->super, "url_passwd", Z_VAR_TYPE_ALIAS | Z_VAR_GET, "request_url_passwd"); z_proxy_var_new(&self->super, "url_host", Z_VAR_TYPE_ALIAS | Z_VAR_GET, "request_url_host"); z_proxy_var_new(&self->super, "url_port", Z_VAR_TYPE_ALIAS | Z_VAR_GET, "request_url_port"); z_proxy_var_new(&self->super, "url_file", Z_VAR_TYPE_ALIAS | Z_VAR_GET, "request_url_file"); z_proxy_var_new(&self->super, "error_response", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET, "error_status"); z_proxy_var_new(&self->super, "auth_by_cookie", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->auth_by_cookie); z_proxy_var_new(&self->super, "auth_cache_time", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->auth_cache_time); z_proxy_var_new(&self->super, "auth_cache_update", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->auth_cache_update); z_proxy_var_new(&self->super, "request_categories", Z_VAR_TYPE_OBJECT | Z_VAR_GET, &self->request_categories); z_proxy_return(self); } /** * http_error_message: * @self: HttpProxy instance * @response_code: HTTP status code to return * @message_code: HTTP message code (one of HTTP_MSG_*) * @infomsg: additional information added into error messages * * This function formats and returns an error page to the client when its * request cannot be fulfilled. It switches to non-persistent mode and * prepares the proxy for shutdown. **/ static gboolean http_error_message(HttpProxy *self, gint response_code, guint message_code, GString *infomsg) { gchar *messages[] = { NULL, "clientsyntax.html", "serversyntax.html", "policysyntax.html", "policyviolation.html", "invalidurl.html", "connecterror.html", "ioerror.html", "auth.html", "clienttimeout.html", "servertimeout.html", "badcontent.html", "ftperror.html", "redirect.html" }; gchar response[256], filename[256]; gchar *error_msg; z_proxy_enter(self); if (message_code >= (sizeof(messages) / sizeof(char *))) { /*LOG This message indicates that Zorp caught an invalid error code internally. Please report this event to the Zorp QA team (at devel@balabit.com). */ z_proxy_log(self, HTTP_ERROR, 2, "Internal error, error code out of range; error_code='%d'", message_code); z_proxy_return(self, FALSE); } if (message_code == 0) z_proxy_return(self, TRUE); if (self->proto_version[EP_CLIENT] >= 0x0100) { g_snprintf(response, sizeof(response), "HTTP/1.0 %d %s\r\n", response_code, self->error_msg->len > 0 ? self->error_msg->str : "Error encountered"); if (http_write(self, EP_CLIENT, response, strlen(response)) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); /* error writing error message is already sent by http_write */ /* FIXME: we should not use self->headers[EP_SERVER] for this purpose */ g_string_truncate(self->headers[EP_SERVER].flat, 0); if (!self->transparent_mode) g_string_append(self->headers[EP_SERVER].flat, "Proxy-Connection: close\r\n"); else g_string_append(self->headers[EP_SERVER].flat, "Connection: close\r\n"); g_string_append(self->headers[EP_SERVER].flat, self->error_headers->str); g_string_append(self->headers[EP_SERVER].flat, "Content-Type: text/html\r\n\r\n"); if (http_write(self, EP_CLIENT, self->headers[EP_SERVER].flat->str, self->headers[EP_SERVER].flat->len) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); } if ((self->request_flags & HTTP_REQ_FLG_HEAD)) z_proxy_return(self, TRUE); /* we are responding to a HEAD request, do not return a body */ if (self->error_files_directory->len) g_snprintf(filename, sizeof(filename), "%s/%s", self->error_files_directory->str, messages[message_code]); else g_snprintf(filename, sizeof(filename), ZORP_DATADIR "/http/" "%s/%s", self->super.language->str, messages[message_code]); if (self->error_silent) { /*LOG This message reports that Zorp would send the given error page to the browser, if silent mode would not be enabled. It is likely that some protocol/configuration/proxy error occurred. */ z_proxy_log(self, HTTP_DEBUG, 6, "An error occurred, would serve error file, but silent mode is enabled; filename='%s'", filename); z_proxy_return(self, FALSE); } /*LOG This message reports that Zorp is sending the given error page to the clients browser. It is likely that some protocol/configuration/proxy error occurred. */ z_proxy_log(self, HTTP_DEBUG, 6, "An error occurred, serving error file; filename='%s'", filename); error_msg = z_error_loader_format_file(filename, infomsg->str, Z_EF_ESCAPE_HTML, NULL, NULL); if (error_msg) { http_write(self, EP_CLIENT, error_msg, strlen(error_msg)); g_free(error_msg); } z_proxy_return(self, TRUE); } /** * http_client_stream_init: * @self: HttpProxy instance * * This function is called upon startup to initialize our client stream. **/ static gboolean http_client_stream_init(HttpProxy *self) { ZStream *tmpstream; z_proxy_enter(self); z_proxy_enter(self); tmpstream = self->super.endpoints[EP_CLIENT]; self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF | ZRL_PARTIAL_READ); z_stream_unref(tmpstream); /* timeout is initialized after the config event */ z_proxy_return(self, TRUE); } /** * http_server_stream_init: * @self: HttpProxy instance * * This function is called upon startup to initialize our server stream. **/ static gboolean http_server_stream_init(HttpProxy *self) { ZStream *tmpstream; z_proxy_enter(self); z_proxy_enter(self); tmpstream = self->super.endpoints[EP_SERVER]; self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF | ZRL_PARTIAL_READ); z_stream_unref(tmpstream); self->super.endpoints[EP_SERVER]->timeout = self->timeout; z_proxy_return(self, TRUE); } /** * http_server_stream_is_initialized: * @self: HttpProxy instance * * This function returns whether or not the server stream has already * been initialized. Currently, it just checks if a StreamLine has been * pushed onto the stack. */ static gboolean http_server_stream_is_initialized(HttpProxy *self) { gboolean res; z_proxy_enter(self); res = (z_stream_search_stack(self->super.endpoints[EP_SERVER], G_IO_IN, Z_CLASS(ZStreamLine)) != NULL); z_proxy_return(self, res); } GIOStatus http_write(HttpProxy *self, guint side, gchar *buf, size_t buflen) { GIOStatus res; gsize bytes_written; z_proxy_enter(self); if (!self->super.endpoints[side]) { /*LOG This message reports that Zorp was about to write to an invalid stream. Please report this event to the Zorp QA team (at devel@balabit.com). */ z_proxy_log(self, HTTP_ERROR, 1, "Error writing stream, stream is NULL; side='%s'", side == EP_CLIENT ? "client" : "server"); z_proxy_return(self, G_IO_STATUS_ERROR); } res = z_stream_write(self->super.endpoints[side], buf, buflen, &bytes_written, NULL); if (res != G_IO_STATUS_NORMAL || buflen != bytes_written) { /* FIXME: move this to a separate function */ /*LOG This message reports that Zorp was unable to write to the given stream. It is likely that the peer closed the connection unexpectedly. */ z_proxy_log(self, HTTP_ERROR, 1, "Error writing stream; side='%s', res='%x', error='%s'", side == EP_CLIENT ? "client" : "server", res, g_strerror(errno)); self->error_code = HTTP_MSG_IO_ERROR; self->error_status = 502; g_string_sprintf(self->error_info, "Error writing to %s (%s)", side == EP_CLIENT ? "client" : "server", g_strerror(errno)); z_proxy_return(self, G_IO_STATUS_ERROR); } z_proxy_return(self, res); } static int http_parse_connection_hdr_value(HttpProxy *self G_GNUC_UNUSED, HttpHeader *hdr) { z_proxy_enter(self); if (strcasecmp(hdr->value->str, "keep-alive") == 0) z_proxy_return(self, HTTP_CONNECTION_KEEPALIVE); else if (strcasecmp(hdr->value->str, "close") == 0) z_proxy_return(self, HTTP_CONNECTION_CLOSE); z_proxy_log(self, HTTP_ERROR, 4, "Unknown connection header value; value='%s'", hdr->value->str); z_proxy_return(self, HTTP_CONNECTION_UNKNOWN); } static void http_assign_connection_hdr_value(HttpProxy *self, GString *value) { z_proxy_enter(self); if (self->connection_mode == HTTP_CONNECTION_KEEPALIVE) g_string_assign(value, "keep-alive"); else if (self->connection_mode == HTTP_CONNECTION_CLOSE) g_string_assign(value, "close"); z_proxy_return(self); } static inline gboolean http_parent_proxy_enabled(HttpProxy *self) { return !!self->parent_proxy->len; } static gboolean http_process_base64(gchar *dst, guint dstlen, gchar *src, guint srclen) { ZCode *auth_line; z_enter(); auth_line = z_code_base64_decode_new(0, FALSE); if (!z_code_transform(auth_line, src, srclen) || !z_code_finish(auth_line)) { z_code_free(auth_line); z_return(FALSE); } dstlen = z_code_get_result(auth_line, dst, dstlen-1); dst[dstlen] = 0; z_code_free(auth_line); z_return(TRUE); } /* FIXME: optimize header processing a bit (no need to copy hdr into a buffer) */ static gboolean http_process_auth_info(HttpProxy *self, HttpHeader *h, ZorpAuthInfo *auth_info) { gchar userpass[128]; gchar *p; gchar **up; z_proxy_enter(self); if (self->old_auth_header->len && strcmp(h->value->str, self->old_auth_header->str) == 0) z_proxy_return(self, TRUE); if (strncmp(h->value->str, "Basic", 5) != 0) { /*LOG This message indicates that the client tried to use the given unsupported HTTP authentication. Currently only Basic HTTP authentication is supported by Zorp. */ z_proxy_log(self, HTTP_ERROR, 3, "Only Basic authentication is supported; authentication='%s'", h->value->str); /* not basic auth */ z_proxy_return(self, FALSE); } p = h->value->str + 5; while (*p == ' ') p++; if (!http_process_base64(userpass, sizeof(userpass), p, strlen(p))) { /*LOG This message indicates that the client sent a malformed username:password field, during the authentication phase. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Invalid base64 encoded username:password pair;"); z_proxy_return(self, FALSE); } up = g_strsplit(userpass, ":", 2); if (up) { gboolean res; gchar **groups = NULL; z_policy_lock(self->super.thread); res = z_auth_provider_check_passwd(self->auth, self->super.session_id, up[0], up[1], &groups, &self->super); z_policy_unlock(self->super.thread); if (res) { res = z_proxy_user_authenticated_default(&self->super, up[0], (gchar const **) groups); g_string_assign(self->old_auth_header, h->value->str); g_mutex_lock(auth_mutex); if (self->auth_cache_time > 0) { auth_info->last_auth_time = time(NULL); } g_mutex_unlock(auth_mutex); } g_strfreev(up); g_strfreev(groups); z_proxy_return(self, res); } /*LOG This message indicates that the username:password field received during authentication was malformed. */ z_proxy_log(self, HTTP_VIOLATION, 2, "No colon is found in the decoded username:password pair;"); z_proxy_return(self, FALSE); } static gboolean http_rewrite_host_header(HttpProxy *self, gchar *host, gint host_len, guint port) { HttpHeader *h; z_proxy_enter(self); if (self->rewrite_host_header && http_lookup_header(&self->headers[EP_CLIENT], "Host", &h)) { /* NOTE: the constant 80 below is intentional, if * default_port is changed we still have to send * correct host header (containing port number) */ if (port != 80 && port != 0) g_string_sprintf(h->value, "%.*s:%d", host_len, host, port); else g_string_sprintf(h->value, "%.*s", host_len, host); } z_proxy_return(self, TRUE); } static gboolean http_format_request(HttpProxy *self, gboolean stacked, GString *request) { gboolean res = TRUE; const gchar *reason; GString *url = g_string_sized_new(self->request_url->len); z_proxy_enter(self); if (self->proto_version[EP_CLIENT] >= 0x0100) { if (self->request_flags & HTTP_REQ_FLG_CONNECT) { g_assert(http_parent_proxy_enabled(self)); http_flat_headers(&self->headers[EP_CLIENT]); g_string_sprintf(request, "%s %s %s\r\n%s\r\n", self->request_method->str, self->request_url->str, self->request_version, self->headers[EP_CLIENT].flat->str); } else { if (!stacked) { http_flat_headers(&self->headers[EP_CLIENT]); if (!http_format_url(&self->request_url_parts, url, http_parent_proxy_enabled(self), self->permit_unicode_url, self->use_canonicalized_urls, &reason)) res = FALSE; #define g_string_append_gstring(r, s) g_string_append_len(r, s->str, s->len) #define g_string_append_const(r, s) g_string_append_len(r, s, __builtin_strlen(s)) /* slightly less than a page to make sure it fits on a page even with the malloc overhead */ g_string_set_size(request, 4000); g_string_truncate(request, 0); g_string_append_gstring(request, self->request_method); g_string_append_c(request, ' '); g_string_append_gstring(request, url); g_string_append_c(request, ' '); g_string_append(request, self->request_version); g_string_append_const(request, "\r\n"); g_string_append_gstring(request, self->headers[EP_CLIENT].flat); g_string_append_const(request, "\r\n"); } else { http_flat_headers_into(&self->headers[EP_CLIENT], request); } } } else { if (!http_format_url(&self->request_url_parts, url, FALSE, self->permit_unicode_url, self->use_canonicalized_urls, &reason)) res = FALSE; else g_string_sprintf(request, "%s %s\r\n", self->request_method->str, url->str); } if (!res) z_proxy_log(self, HTTP_ERROR, 3, "Error reformatting requested URL; url='%s', reason='%s'", self->request_url->str, reason); g_string_free(url, TRUE); z_proxy_return(self, res); } static gboolean http_format_early_request(HttpProxy *self, gboolean stacked, GString *preamble) { if (stacked) return http_format_request(self, stacked, preamble); else g_string_assign(preamble, ""); return TRUE; } static gboolean http_fetch_request(HttpProxy *self) { gchar *line; gsize line_length; gint empty_lines = 0; guint res; z_proxy_enter(self); /* FIXME: this can probably be removed as http_fetch_header does this */ http_clear_headers(&self->headers[EP_CLIENT]); while (empty_lines < HTTP_MAX_EMPTY_REQUESTS) { self->super.endpoints[EP_CLIENT]->timeout = self->timeout_request; res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &line, &line_length, NULL); self->super.endpoints[EP_CLIENT]->timeout = self->timeout; if (res == G_IO_STATUS_EOF) { self->error_code = HTTP_MSG_OK; z_proxy_return(self, FALSE); } if (res != G_IO_STATUS_NORMAL) { self->error_code = HTTP_MSG_OK; if (errno == ETIMEDOUT) { if (self->request_count == 0) { self->error_code = HTTP_MSG_CLIENT_TIMEOUT; self->error_status = 408; } } z_proxy_return(self, FALSE); } if (line_length != 0) break; empty_lines++; } if (!http_split_request(self, line, line_length)) { g_string_assign(self->error_info, "Invalid request line."); /*LOG This message indicates that the client sent an invalid HTTP request to the proxy. */ z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid HTTP request received; line='%.*s'", (gint)line_length, line); z_proxy_return(self, FALSE); } self->request_flags = http_proto_request_lookup(self->request_method->str); if (!http_parse_version(self, EP_CLIENT, self->request_version)) z_proxy_return(self, FALSE); /* parse version already logged */ if (!http_fetch_headers(self, EP_CLIENT)) z_proxy_return(self, FALSE); /* fetch headers already logged */ if (self->rerequest_attempts > 0) { HttpHeader *transfer_encoding_hdr, *content_length_hdr; gboolean has_data = FALSE; if (http_lookup_header(&self->headers[EP_CLIENT], "Transfer-Encoding", &transfer_encoding_hdr)) has_data = TRUE; if (http_lookup_header(&self->headers[EP_CLIENT], "Content-Length", &content_length_hdr)) has_data = TRUE; self->request_data_stored = TRUE; if (self->request_data) z_blob_truncate(self->request_data, 0, -1); if (has_data) { gchar session_id[MAX_SESSION_ID]; ZStream *blob_stream; gboolean success; if (!self->request_data) self->request_data = z_blob_new(NULL, 0); if (!self->request_data) z_proxy_return(self, FALSE); g_snprintf(session_id, sizeof(session_id), "%s/post", self->super.session_id); blob_stream = z_stream_blob_new(self->request_data, session_id); blob_stream->timeout = -1; /* fetch data associated with request */ success = http_data_transfer(self, HTTP_TRANSFER_TO_BLOB, EP_CLIENT, self->super.endpoints[EP_CLIENT], EP_SERVER, blob_stream, FALSE, FALSE, http_format_early_request); z_stream_unref(blob_stream); if (!success) z_proxy_return(self, FALSE); } } z_proxy_return(self, TRUE); } static gboolean http_remove_old_auth(gpointer key G_GNUC_UNUSED, gpointer value, gpointer user_data) { ZorpAuthInfo *real_value = (ZorpAuthInfo *)value; time_t max_time = MAX(MAX(real_value->last_auth_time, real_value->accept_credit), real_value->create_time); time_t cut_time = GPOINTER_TO_UINT(user_data); return max_time < cut_time; } static inline gchar * http_process_create_realm(HttpProxy *self, time_t now, gchar *buf, guint buflen) { if (self->max_auth_time > 0) g_snprintf(buf, buflen, "Basic realm=\"%s (id:%x)\"", self->auth_realm->str, (int)now); else g_snprintf(buf, buflen, "Basic realm=\"%s\"", self->auth_realm->str); return buf; } static gboolean http_get_client_info(HttpProxy *self, gchar *key, guint key_length) { ZCode *coder; guchar raw_key[32]; gsize pos; GHashTable *cookie_hash; gchar *our_value; cookie_hash = http_parse_header_cookie(&self->headers[EP_CLIENT]); if (cookie_hash) { our_value = g_hash_table_lookup(cookie_hash, "ZorpRealm"); if (our_value) { g_strlcpy(key, our_value, key_length); goto exit; } } if (!z_random_sequence_get(Z_RANDOM_STRONG, raw_key, sizeof(raw_key))) { return FALSE; } coder = z_code_base64_encode_new(256, 255); if (coder == NULL) { return FALSE; } if (!z_code_transform(coder, raw_key, sizeof(raw_key)) || !z_code_finish(coder)) { return FALSE; } pos = z_code_get_result(coder, key, key_length - 1); key[pos-2] = 0; exit: return TRUE; } static gboolean http_check_name(HttpProxy *self) { z_proxy_enter(self); ZProxyHostIface *host_iface = Z_CAST(z_proxy_find_iface(&self->super, Z_CLASS(ZProxyHostIface)), ZProxyHostIface); if (host_iface == NULL) host_iface = Z_CAST(z_proxy_find_iface(self->super.parent_proxy, Z_CLASS(ZProxyHostIface)), ZProxyHostIface); if (host_iface) { gchar error_reason[256]; if (!z_proxy_host_iface_check_name(host_iface, self->remote_server->str, error_reason, sizeof(error_reason))) { z_proxy_log(self, HTTP_ERROR, 3, "Error checking hostname; error='%s'", error_reason); g_string_assign(self->error_info, error_reason); z_proxy_return(self, FALSE); } z_object_unref(&host_iface->super); } z_proxy_return(self, TRUE); } static gboolean http_process_request(HttpProxy *self) { HttpHeader *h; const gchar *reason; static time_t prev_cleanup = 0; ZorpAuthInfo *auth_info; /* The variable below is to keep the code simple. There are some situation when Zorp should put a Set-Cookie header into the answer. But the condition is not too simple. And the good place when all the condititons could evaluate esaily is too early to get server domain name which is needed for the header. So the header should be set in a latter place. This variable is to know that this should happen. */ gboolean need_cookie_header = FALSE; gchar client_key[512]; z_proxy_enter(self); if (self->proto_version[EP_CLIENT] > 0x0100) self->connection_mode = HTTP_CONNECTION_KEEPALIVE; else self->connection_mode = HTTP_CONNECTION_CLOSE; if (self->auth) { if (self->proto_version[EP_CLIENT] >= 0x0100) { ZSockAddr *client_addr; struct in_addr c_addr; time_t now = time(NULL); gchar buf[4096]; if (self->auth_by_cookie) { if (!http_get_client_info(self, client_key, sizeof(client_key))) g_assert_not_reached(); } else { z_proxy_get_addresses(&self->super, NULL, &client_addr, NULL, NULL, NULL, NULL); c_addr = z_sockaddr_inet_get_address(client_addr); z_sockaddr_unref(client_addr); z_inet_ntoa(client_key, sizeof(client_key), c_addr); } g_mutex_lock(auth_mutex); if (now > prev_cleanup + (2 * MAX(self->max_auth_time, self->auth_cache_time))) { g_hash_table_foreach_remove(auth_hash, http_remove_old_auth, GUINT_TO_POINTER(now - (2 * MAX(self->max_auth_time, self->auth_cache_time)))); prev_cleanup = now; } auth_info = g_hash_table_lookup(auth_hash, client_key); if (auth_info == NULL) { auth_info = g_new0(ZorpAuthInfo, 1); auth_info->create_time = now; g_hash_table_insert(auth_hash, g_strdup(client_key), auth_info); } if (self->auth_cache_time > 0 && auth_info->last_auth_time + self->auth_cache_time > now) { if (self->auth_cache_update) { auth_info->last_auth_time = now; } g_mutex_unlock(auth_mutex); } else { /* authentication is required */ g_string_truncate(self->auth_header_value, 0); h = NULL; g_mutex_unlock(auth_mutex); if (self->transparent_mode) { if ((auth_info->accept_credit > 0 && auth_info->accept_credit < now) || !http_lookup_header(&self->headers[EP_CLIENT], "Authorization", &h) || !http_process_auth_info(self, h, auth_info)) { g_mutex_lock(auth_mutex); if (self->max_auth_time > 0) auth_info->accept_credit = now + self->max_auth_time; g_mutex_unlock(auth_mutex); self->error_code = HTTP_MSG_AUTH_REQUIRED; self->error_status = 401; g_string_sprintf(self->error_msg, "Authentication is required."); g_string_sprintfa(self->error_headers, "WWW-Authenticate: %s\r\n", http_process_create_realm(self, now, buf, sizeof(buf))); z_proxy_return(self, FALSE); } } else if ((auth_info->accept_credit > 0 && auth_info->accept_credit < now) || !http_lookup_header(&self->headers[EP_CLIENT], "Proxy-Authorization", &h) || !http_process_auth_info(self, h, auth_info)) { g_mutex_lock(auth_mutex); if (self->max_auth_time > 0) auth_info->accept_credit = now + self->max_auth_time; g_mutex_unlock(auth_mutex); self->error_code = HTTP_MSG_AUTH_REQUIRED; self->error_status = 407; g_string_sprintf(self->error_msg, "Authentication is required."); g_string_sprintfa(self->error_headers, "Proxy-Authenticate: %s\r\n", http_process_create_realm(self, now, buf, sizeof(buf))); z_proxy_return(self, FALSE); } g_string_assign(self->auth_header_value, h->value->str); if (self->auth_by_cookie) need_cookie_header = TRUE; } } else { z_proxy_log(self, HTTP_POLICY, 2, "Authentication required, but client requested HTTP/0.9 which does not support authentication;"); z_proxy_return(self, FALSE); } } if (self->request_flags & HTTP_REQ_FLG_CONNECT) { if (self->proto_version[EP_CLIENT] >= 0x0100) { self->request_type = HTTP_REQTYPE_PROXY; z_proxy_return(self, TRUE); } self->error_code = HTTP_MSG_CLIENT_SYNTAX; g_string_sprintf(self->error_info, "CONNECT method requires HTTP/1.0 or later"); /*LOG This message indicates that the client sent a CONNECT method request, but it is only supported for HTTP/1.0 or later. */ z_proxy_log(self, HTTP_VIOLATION, 1, "CONNECT method without version specification;"); z_proxy_return(self, FALSE); } /* detect request type and set connection header in self */ self->connection_hdr = NULL; if (self->proto_version[EP_CLIENT] < 0x0100) { /* no proxy protocol for version 0.9 */ self->request_type = HTTP_REQTYPE_SERVER; } else { HttpHeader *pconn_hdr = NULL, *conn_hdr = NULL; http_lookup_header(&self->headers[EP_CLIENT], "Proxy-Connection", &pconn_hdr); http_lookup_header(&self->headers[EP_CLIENT], "Connection", &conn_hdr); if (pconn_hdr && conn_hdr) { if (!self->permit_both_connection_headers) { /* both Proxy-Connection & Connection headers ... */ self->error_code = HTTP_MSG_CLIENT_SYNTAX; g_string_sprintf(self->error_info, "Both Proxy-Connection and Connection headers exist."); /*LOG This message indicates that the client sent both Connection and Proxy-Connection headers, but these are mutually exclusive. It is likely sent by a buggy proxy/browser on the client side. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Both Proxy-Connection and Connection headers exist;"); z_proxy_return(self, FALSE); } else { if (self->request_url->str[0] == '/') self->request_type = HTTP_REQTYPE_SERVER; else self->request_type = HTTP_REQTYPE_PROXY; } } else if (pconn_hdr) { self->request_type = HTTP_REQTYPE_PROXY; } else if (conn_hdr) { self->request_type = HTTP_REQTYPE_SERVER; } else if (self->request_url->str[0] != '/') { /* neither Connection nor Proxy-Connection header exists, and URI doesn't seem to be a simple filename */ self->request_type = HTTP_REQTYPE_PROXY; } else { /* default */ self->request_type = HTTP_REQTYPE_SERVER; } if (self->request_type == HTTP_REQTYPE_SERVER) { if (pconn_hdr) pconn_hdr->present = FALSE; self->connection_hdr = conn_hdr; } if (self->request_type == HTTP_REQTYPE_PROXY) { if (conn_hdr) conn_hdr->present = FALSE; self->connection_hdr = pconn_hdr; } } if (self->connection_hdr) { /* connection_mode overridden by connection header */ gint connection_mode = http_parse_connection_hdr_value(self, self->connection_hdr); if (connection_mode != HTTP_CONNECTION_UNKNOWN) self->connection_mode = connection_mode; } if (self->transparent_mode && ((self->request_type == HTTP_REQTYPE_PROXY && !self->permit_proxy_requests) || (self->request_type == HTTP_REQTYPE_SERVER && !self->permit_server_requests))) { /* */ self->error_code = HTTP_MSG_POLICY_VIOLATION; g_string_sprintf(self->error_info, "%s requests not permitted in transparent mode.", self->request_type == HTTP_REQTYPE_SERVER ? "server" : "proxy"); /*LOG This message indicates that the client sent the given type request, which is not permitted by the policy. Check the permit_proxy_requests and the permit_server_requests attributes. */ z_proxy_log(self, HTTP_POLICY, 2, "This request type is not permitted in transparent mode; request_type='%s'", self->request_type == HTTP_REQTYPE_SERVER ? "server" : "proxy"); z_proxy_return(self, FALSE); } if (http_lookup_header(&self->headers[EP_CLIENT], "Host", &h)) g_string_assign(self->remote_server, h->value->str); else g_string_truncate(self->remote_server, 0); if (self->transparent_mode) { if (self->request_url->str[0] == '/') { gchar buf[self->remote_server->len + 32]; /* no protocol description */ if (!self->remote_server->len) { if (!self->require_host_header) { /* no host header */ /*LOG This message indicates that no Host header was sent by the client. As the content of the host header is used to reconstruct the requested URL, the request_url attribute will refer to a host named 'unknown'. */ z_proxy_log(self, HTTP_VIOLATION, 4, "No host header in transparent request, 'unknown' is used instead;"); g_string_assign(self->remote_server, "unknown"); } else { self->error_code = HTTP_MSG_CLIENT_SYNTAX; if (self->proto_version[EP_CLIENT] < 0x0100) { g_string_sprintf(self->error_info, "'Host:' header is required, and HTTP/0.9 can't transfer headers."); /*LOG This message indicates that an HTTP/0.9 request was sent by the client, and Host header is required by the policy, but HTTP/0.9 does not support headers. Check the require_host_header attribute. */ z_proxy_log(self, HTTP_POLICY, 2, "'Host:' header is required, and HTTP/0.9 can't transfer headers;"); } else { g_string_sprintf(self->error_info, "No 'Host:' header in request, and policy requires this."); /*LOG This message indicates that no Host header was sent by the client, but it was required by the policy. Check the require_host_header attribute. */ z_proxy_log(self, HTTP_POLICY, 2, "No 'Host:' header in request, and policy requires this;"); } z_proxy_return(self, FALSE); } } g_snprintf(buf, sizeof(buf), "http://%s", self->remote_server->str); g_string_prepend(self->request_url, buf); } } if (!http_parse_url(&self->request_url_parts, self->permit_unicode_url, self->permit_invalid_hex_escape, FALSE, self->request_url->str, &reason)) { /* invalid URL */ self->error_code = HTTP_MSG_INVALID_URL; g_string_sprintf(self->error_info, "Badly formatted url: %s", self->request_url->str); /*LOG This message indicates that there was an error parsing an already canonicalized URL. Please report this event to the Zorp QA team (at devel@balabit.com) */ z_proxy_log(self, HTTP_ERROR, 1, "Error parsing URL; url='%s', reason='%s'", self->request_url->str, reason); z_proxy_return(self, FALSE); } if (!http_format_url(&self->request_url_parts, self->request_url, TRUE, self->permit_unicode_url, TRUE, &reason)) { self->error_code = HTTP_MSG_INVALID_URL; g_string_sprintf(self->error_info, "Error canonicalizing url (%s): %s", reason, self->request_url->str); /*LOG This message indicates that there was an error parsing an already canonicalized URL. Please report this event to the Zorp QA team (at devel@balabit.com) */ z_proxy_log(self, HTTP_ERROR, 1, "Error parsing URL; url='%s', reason='%s'", self->request_url->str, reason); z_proxy_return(self, FALSE); } self->remote_port = self->request_url_parts.port; if (need_cookie_header) { gchar *hostname = self->request_url_parts.host->str; gchar *startpos = hostname; gchar *dotpos = strchr(startpos, '.'); gchar *nextdotpos = NULL; if (dotpos != NULL) nextdotpos = strchr(dotpos + 1, '.'); while (nextdotpos != NULL) { startpos = dotpos; dotpos = nextdotpos; nextdotpos = strchr(dotpos + 1, '.'); } self->append_cookie = g_string_sized_new(32); g_string_printf(self->append_cookie, "ZorpRealm=%s; path=/; domain=%s", client_key, startpos); } z_proxy_return(self, TRUE); } static gboolean http_process_filtered_request(HttpProxy *self) { if ((self->request_flags & HTTP_REQ_FLG_CONNECT)) { self->server_protocol = HTTP_PROTO_HTTP; } else if (http_parent_proxy_enabled(self) && (strcasecmp(self->request_url_parts.scheme->str, "http") == 0 || strcasecmp(self->request_url_parts.scheme->str, "ftp") == 0 || strcasecmp(self->request_url_parts.scheme->str, "cache_object") == 0)) { self->server_protocol = HTTP_PROTO_HTTP; } else if (!http_parent_proxy_enabled(self) && strcasecmp(self->request_url_parts.scheme->str, "ftp") == 0) { if (self->permit_ftp_over_http) { self->server_protocol = HTTP_PROTO_FTP; } else { /*LOG This message indicates that a client tried to use FTP over HTTP which is not allowed by default. Either set a parent proxy or enable the permit_ftp_over_http attribute. */ z_proxy_log(self, HTTP_POLICY, 2, "Client attempted to use FTP over HTTP, which is currently disabled;"); self->error_code = HTTP_MSG_CLIENT_SYNTAX; z_proxy_return(self, FALSE); } } else if (!http_parent_proxy_enabled(self) && strcasecmp(self->request_url_parts.scheme->str, "http") == 0) { self->server_protocol = HTTP_PROTO_HTTP; } else { /* unsupported protocol */ self->error_code = HTTP_MSG_CLIENT_SYNTAX; g_string_sprintf(self->error_info, "Unsupported scheme: %s", self->request_url_parts.scheme->str); /*LOG This message indicates that the requested URL refers to an unsupported protocol scheme. Zorp currently knows about http and cache_object protocols, and can support the ftp protocol if a parent_proxy supporting ftp over http tunneling is present. */ z_proxy_log(self, HTTP_ERROR, 3, "Unsupported scheme in URL; proto='%s'", self->request_url_parts.scheme->str); z_proxy_return(self, FALSE); } if (self->request_url_parts.host->len > self->max_hostname_length) { self->error_code = HTTP_MSG_CLIENT_SYNTAX; g_string_sprintf(self->error_info, "Too long hostname in URL: %s", self->request_url_parts.host->str); /*LOG This message indicates that the HTTP request was rejected because the hostname part in the URL was too long. You can increase the permitted limit by changing the max_hostname_length attribute. */ z_proxy_log(self, HTTP_POLICY, 2, "Too long hostname in URL; host='%s', length='%" G_GSIZE_FORMAT "', max_hostname_length='%d'", self->request_url_parts.host->str, self->request_url_parts.host->len, self->max_hostname_length); z_proxy_return(self, FALSE); } if (self->remote_port == 0) { if (self->server_protocol == HTTP_PROTO_HTTP) self->remote_port = self->default_http_port; else self->remote_port = self->default_ftp_port; } if (!(self->request_flags & HTTP_REQ_FLG_CONNECT)) http_rewrite_host_header(self, self->request_url_parts.host->str, self->request_url_parts.host->len, self->remote_port); if (http_parent_proxy_enabled(self)) { self->remote_port = self->parent_proxy_port; g_string_assign(self->remote_server, self->parent_proxy->str); } else { g_string_assign(self->remote_server, self->request_url_parts.host->str); } /*LOG This is an accounting message that reports the requested method and URL. */ z_proxy_log(self, HTTP_ACCOUNTING, 4, "Accounting; command='%s', url='%s'", self->request_method->str, self->request_url->str); z_proxy_return(self, TRUE); } /* FIXME: this code should be converted into an explicit referral to the * modified headers, e.g. instead of looping over all the headers, lookup * the necessary headers using http_lookup_header and modify the returned * values */ static guint http_request_filter_headers(HttpProxy *self, GString *name, GString *value) { gint res = HTTP_HDR_ACCEPT; z_proxy_enter(self); switch (self->request_type) { case HTTP_REQTYPE_SERVER: /* if we have a parent proxy, Connection -> Proxy-Connection * otherwise leave it as is */ if (strcasecmp(name->str, "Connection") == 0) { if (http_parent_proxy_enabled(self)) g_string_assign(name, "Proxy-Connection"); http_assign_connection_hdr_value(self, value); } else if (strcasecmp(name->str, "Authorization") == 0) { if (self->auth) { /* if inband authentication was performed, drop the * authentication header unless forwarding was explicitly * requested */ if (self->auth_forward) { g_string_assign(value, self->auth_header_value->str); /* if the upstream is a proxy, forward it as a * Proxy-Authorization header */ if (http_parent_proxy_enabled(self)) g_string_assign(name, "Proxy-Authorization"); } else { res = HTTP_HDR_DROP; } } } break; case HTTP_REQTYPE_PROXY: /* if we have a parent proxy leave it as is * otherwise Proxy-Connection -> Connection */ if (strcasecmp(name->str, "Proxy-Connection") == 0) { if (http_parent_proxy_enabled(self) == 0) g_string_assign(name, "Connection"); http_assign_connection_hdr_value(self, value); } else if (strcasecmp(name->str, "Proxy-Authorization") == 0) { if (self->auth) { /* if inband authentication was performed, drop the * authentication header unless forwarding was explicitly * requested */ if (self->auth_forward) { g_string_assign(value, self->auth_header_value->str); /* if the upstream is not a proxy, forward it as a * Authorization header */ if (!http_parent_proxy_enabled(self)) g_string_assign(name, "Authorization"); } else { res = HTTP_HDR_DROP; } } } break; } z_proxy_return(self, res); } static gboolean http_filter_request(HttpProxy *self) { ZPolicyObj *f; gint rc; z_proxy_enter(self); f = g_hash_table_lookup(self->request_method_policy, self->request_method->str); if (!f) f = g_hash_table_lookup(self->request_method_policy, "*"); if (f) { ZPolicyObj *handler, *res; gchar *errmsg; guint filter_type; z_policy_lock(self->super.thread); if (!z_policy_tuple_get_verdict(f, &filter_type)) { /*LOG This message indicates that the request hash contains an invalid item for the given request method. Check your Zorp configuration. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid item in request hash; method='%s'", self->request_method->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } z_policy_unlock(self->super.thread); g_string_sprintf(self->error_info, "Method %s denied by policy", self->request_method->str); switch (filter_type) { case HTTP_REQ_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(f, "(iO)", &filter_type, &handler)) { /*LOG This message indicates that the request hash contains an invalid POLICY tuple for the given request method. It should contain a valid call-back function in the tuple. */ z_proxy_log(self, HTTP_POLICY, 1, "Error parsing HTTP_REQ_POLICY tuple in request hash; method='%s'", self->request_method->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } res = z_policy_call_object(handler, z_policy_var_build("(sss)", self->request_method->str, self->request_url->str, self->request_version), self->super.session_id); if (!res || !z_policy_var_parse(res, "i", &rc)) { rc = HTTP_REQ_REJECT; g_string_assign(self->error_info, "Error in policy handler, or returned value not integer;"); } z_policy_var_unref(res); z_policy_unlock(self->super.thread); break; case HTTP_REQ_REJECT: errmsg = NULL; z_policy_lock(self->super.thread); if (!z_policy_var_parse_tuple(f, "i|s", &filter_type, &errmsg)) { /*LOG This message indicates that the request hash contains an invalid REJECT tuple for the given request method. It should contain an error message, which is sent back to the client. */ z_proxy_log(self, HTTP_POLICY, 1, "Error parsing HTTP_REQ_REJECT in request hash; req='%s'", self->request_method->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } z_policy_unlock(self->super.thread); if (errmsg) g_string_assign(self->error_info, errmsg); /* fallthrough */ case HTTP_REQ_ACCEPT: case HTTP_REQ_DENY: case HTTP_REQ_ABORT: /* dropped command */ rc = filter_type; break; default: /*LOG This message indicates that the request hash contains an invalid action for the given request method. Check your Zorp configuration. */ z_proxy_log(self, HTTP_POLICY, 1, "Unknown request hash item; req='%s'", self->request_method->str); z_proxy_return(self, FALSE); } switch (rc) { case HTTP_REQ_ACCEPT: g_string_truncate(self->error_info, 0); break; default: /*LOG This log message indicates that the specified HTTP request was not permitted by your policy. */ z_proxy_log(self, HTTP_POLICY, 2, "Request not permitted by policy; req='%s'", self->request_method->str); self->error_code = HTTP_MSG_POLICY_VIOLATION; z_proxy_return(self, FALSE); } if (self->proto_version[EP_CLIENT] >= 0x0100) { if (!http_filter_headers(self, EP_CLIENT, http_request_filter_headers)) z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } self->error_code = HTTP_MSG_POLICY_VIOLATION; g_string_sprintf(self->error_info, "Method %s denied by policy", self->request_method->str); /*LOG This log message indicates that the specified HTTP request was not permitted by your policy. */ z_proxy_log(self, HTTP_POLICY, 2, "Request not permitted by policy; req='%s'", self->request_method->str); z_proxy_return(self, FALSE); } static gboolean http_server_stream_ready(HttpProxy *self) { gboolean res = FALSE; z_proxy_enter(self); if (self->super.endpoints[EP_SERVER]) { GIOStatus rc; gchar buf[1]; gsize bytes_read; ZStream *stream = self->super.endpoints[EP_SERVER]; z_stream_set_nonblock(stream, TRUE); rc = z_stream_read(stream, &buf, sizeof(buf), &bytes_read, NULL); z_stream_set_nonblock(stream, FALSE); if (rc == G_IO_STATUS_NORMAL || rc == G_IO_STATUS_AGAIN) { res = TRUE; if (bytes_read > 0 && !z_stream_unget(stream, buf, bytes_read, NULL)) { z_proxy_log(self, HTTP_ERROR, 2, "Error while checking if server stream is ready, buffer full"); res = FALSE; } } } z_proxy_return(self, res); } gboolean http_connect_server(HttpProxy *self) { z_proxy_enter(self); if (!self->super.endpoints[EP_SERVER] || !http_server_stream_ready(self) || (!self->transparent_mode && (strcasecmp(self->remote_server->str, self->connected_server->str) != 0 || self->remote_port != self->connected_port)) || self->force_reconnect) { gboolean success = FALSE; self->force_reconnect = FALSE; if (self->super.endpoints[EP_SERVER]) { z_stream_shutdown(self->super.endpoints[EP_SERVER], SHUT_RDWR, NULL); z_stream_close(self->super.endpoints[EP_SERVER], NULL); z_stream_unref(self->super.endpoints[EP_SERVER]); self->super.endpoints[EP_SERVER] = NULL; z_proxy_ssl_clear_session(&self->super, EP_SERVER); } g_string_sprintf(self->error_info, "Error establishing connection to %s", self->remote_server->str); if (http_parent_proxy_enabled(self)) { success = z_proxy_connect_server(&self->super, self->parent_proxy->str, self->parent_proxy_port); } else if (self->transparent_mode && self->use_default_port_in_transparent_mode) { success = z_proxy_connect_server(&self->super, self->remote_server->str, self->server_protocol == HTTP_PROTO_HTTP ? self->default_http_port : self->default_ftp_port); } else if (z_port_enabled(self->target_port_range->str, self->remote_port)) { success = z_proxy_connect_server(&self->super, self->remote_server->str, self->remote_port); } else { /*LOG This message indicates that the proxy did not allow addressing the specified port as the target_port_range attribute does not allow it. */ z_proxy_log(self, HTTP_VIOLATION, 2, "Connecting to this port is prohibited by policy; host='%s', port='%d'", self->remote_server->str, self->remote_port); g_string_sprintf(self->error_info, "Connecting to port %d is prohibited by policy.", self->remote_port); success = FALSE; } if (!success) { /* error connecting to server */ self->error_code = HTTP_MSG_CONNECT_ERROR; self->error_status = 502; /* connect_server already logged */ z_proxy_return(self, FALSE); } g_string_assign(self->connected_server, self->remote_server->str); self->connected_port = self->remote_port; } if (!http_server_stream_is_initialized(self) && !http_server_stream_init(self)) { /* should never happen */ /*LOG This message indicates that initializing the server stream failed. Please report this event to the Zorp QA team (at devel@balabit.com). */ z_proxy_log(self, HTTP_ERROR, 1, "Internal error initializing server stream;"); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } static gboolean http_copy_request(HttpProxy *self) { ZStream *blob_stream = NULL; z_proxy_enter(self); if (!http_connect_server(self)) z_proxy_return(self, FALSE); /* connect_server already logs */ if (!http_check_name(self)) { z_proxy_return(self, FALSE); } if (self->request_data_stored && self->request_data && self->request_data->size > 0) { gchar session_id[MAX_SESSION_ID]; g_snprintf(session_id, sizeof(session_id), "%s/post", self->super.session_id); blob_stream = z_stream_blob_new(self->request_data, session_id); blob_stream->timeout = -1; } if (!http_data_transfer(self, blob_stream ? HTTP_TRANSFER_FROM_BLOB : HTTP_TRANSFER_NORMAL, EP_CLIENT, blob_stream ? blob_stream : self->super.endpoints[EP_CLIENT], EP_SERVER, self->super.endpoints[EP_SERVER], FALSE, FALSE, http_format_request)) { /* http_data_transfer already logs */ z_stream_unref(blob_stream); z_proxy_return(self, FALSE); } z_stream_unref(blob_stream); z_proxy_return(self, TRUE); } static gboolean http_fetch_response(HttpProxy *self) { gchar *line; gsize line_length, br; GIOStatus res; gchar status[4]; z_proxy_enter(self); /* FIXME: this can probably be removed as http_fetch_header does this */ http_clear_headers(&self->headers[EP_SERVER]); self->response[0] = 0; self->response_code = -1; if (self->proto_version[EP_CLIENT] < 0x0100) { self->proto_version[EP_SERVER] = 0x0009; z_proxy_return(self, TRUE); } while (self->response_code == -1 || self->response_code == 100 || self->response_code == 102) { self->super.endpoints[EP_SERVER]->timeout = self->timeout_response; res = z_stream_read_chunk(self->super.endpoints[EP_SERVER], status, sizeof(status), &br, NULL); self->super.endpoints[EP_SERVER]->timeout = self->timeout; if (res != G_IO_STATUS_NORMAL) { /* the server closed our connection */ self->error_code = HTTP_MSG_OK; z_proxy_return(self, FALSE); } if (!z_stream_unget(self->super.endpoints[EP_SERVER], status, br, NULL)) { /* error falling back to 0.9 */ /*LOG This message indicates that Zorp was unable to enable HTTP/0.9 compatibility mode, due to the full buffer. If you experience this problem many times, please contact your Zorp support. */ z_proxy_log(self, HTTP_ERROR, 2, "Error in HTTP/0.9 compatibility code, line buffer full;"); z_proxy_return(self, FALSE); } if (br == 4 && memcmp(status, "HTTP", 4) == 0) { res = z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &line_length, NULL); if (res != G_IO_STATUS_NORMAL) { self->error_code = HTTP_MSG_OK; z_proxy_return(self, FALSE); } } else if (self->permit_http09_responses) { self->proto_version[EP_SERVER] = 0x0009; z_proxy_return(self, TRUE); } else { /* HTTP/0.9 is not permitted by policy */ g_string_assign(self->error_info, "Server falled back to HTTP/0.9 which is prohibited by policy."); /*LOG This message indicates that the server sent back HTTP/0.9 response, which is prohibited by the policy. It is likely a buggy or old server. Check the permit_http09_responses attribute. */ z_proxy_log(self, HTTP_POLICY, 2, "Server falled back to HTTP/0.9 which is prohibited by policy;"); z_proxy_return(self, FALSE); } if (!http_split_response(self, line, line_length)) { /*LOG This message indicates the the HTTP status line returned by the server was invalid. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Invalid HTTP response heading; line='%.*s'", (gint)line_length, line); g_string_assign(self->error_info, "Invalid HTTP response heading"); z_proxy_return(self, FALSE); } self->response_flags = http_proto_response_lookup(self->response); if (!http_parse_version(self, EP_SERVER, self->response_version)) { g_string_sprintf(self->error_info, "Invalid HTTP version in response (%.*s)", (gint) line_length, line); /*LOG This message indicates that the server sent the response with an invalid HTTP version. It is likely that the server is buggy. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Error parsing response version; line='%.*s'", (gint) line_length, line); z_proxy_return(self, FALSE); } if (!http_fetch_headers(self, EP_SERVER)) { g_string_assign(self->error_info, "Invalid headers received in response"); z_proxy_return(self, FALSE); } if (self->append_cookie) { http_add_header(&self->headers[EP_SERVER], "Set-Cookie", strlen("Set-Cookie"), self->append_cookie->str, self->append_cookie->len); g_string_free(self->append_cookie, TRUE); self->append_cookie = NULL; } } z_proxy_return(self, TRUE); } static gboolean http_process_response(HttpProxy *self) { HttpHeader *pconn_hdr = NULL, *conn_hdr = NULL; z_proxy_enter(self); /* previously set by http_parse_version */ switch (self->proto_version[EP_SERVER]) { case 0x0009: g_strlcpy(self->response, "200", sizeof(self->response_code)); self->response_code = 200; self->response_flags = 0; self->connection_mode = HTTP_CONNECTION_CLOSE; self->server_connection_mode = HTTP_CONNECTION_CLOSE; z_proxy_return(self, TRUE); case 0x0100: self->server_connection_mode = HTTP_CONNECTION_CLOSE; break; case 0x0101: self->server_connection_mode = HTTP_CONNECTION_KEEPALIVE; break; default: /*LOG This message indicates that the server sent an unsupported protocol version. It is likely that the server is buggy. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Unsupported protocol version; version='0x%04x'", self->proto_version[EP_SERVER]); z_proxy_return(self, FALSE); } /* process connection header */ self->connection_hdr = NULL; http_lookup_header(&self->headers[EP_SERVER], "Proxy-Connection", &pconn_hdr); http_lookup_header(&self->headers[EP_SERVER], "Connection", &conn_hdr); if ((http_parent_proxy_enabled(self) && pconn_hdr) || conn_hdr) { /* override default */ if (http_parent_proxy_enabled(self) && pconn_hdr) { self->connection_hdr = pconn_hdr; if (conn_hdr) conn_hdr->present = FALSE; } else { self->connection_hdr = conn_hdr; if (pconn_hdr) pconn_hdr->present = FALSE; } if (self->connection_mode == HTTP_CONNECTION_KEEPALIVE && http_parse_connection_hdr_value(self, self->connection_hdr) != HTTP_CONNECTION_CLOSE) self->server_connection_mode = HTTP_CONNECTION_KEEPALIVE; else self->server_connection_mode = HTTP_CONNECTION_CLOSE; } else { /* NOTE: there was no appropriate Connection header in the response, if it * is an 1.0 client the connection mode should be changed to * connection close regardless what the client specified in its * connection header. */ gchar *conn_hdr_str = http_parent_proxy_enabled(self) ? "Proxy-Connection" : "Connection"; /* we add an appriopriate connection header just as if we received one * (e.g. it might be rewritten later by * http_response_filter_connection_header), we default to not sending * this new header, it will be made visible when we want to ensure * that our connection mode must be enforced. */ self->connection_hdr = http_add_header(&self->headers[EP_SERVER], conn_hdr_str, strlen(conn_hdr_str), "close", 5); self->connection_hdr->present = FALSE; if (self->proto_version[EP_CLIENT] == 0x0100) { if (self->connection_mode == HTTP_CONNECTION_KEEPALIVE) { /* it is somewhat uncertain what happens when an 1.0 client * requests keepalive and an 1.1 server responds without a * connection header, resolve this uncertainity by adding a * connection header */ self->connection_hdr->present = TRUE; } self->server_connection_mode = HTTP_CONNECTION_CLOSE; } } if (self->request_flags & HTTP_REQ_FLG_CONNECT && self->response_code == 200) self->server_connection_mode = HTTP_CONNECTION_CLOSE; if (!self->keep_persistent && self->server_connection_mode == HTTP_CONNECTION_CLOSE) self->connection_mode = HTTP_CONNECTION_CLOSE; if (self->response_flags & HTTP_RESP_FLG_STOP) z_proxy_return(self, FALSE); /* FIXME: not implemented */ z_proxy_return(self, TRUE); } /** * http_fetch_buffered_data: * @self: HttpProxy instance * * Read all buffered data from the client up to maximum ten 4096 chunks. * This is needed to avoid sending RSTs to connections where the client sent * some unprocessed bytes (like IE 5.0 in the case of POST requests). **/ static void http_fetch_buffered_data(HttpProxy *self) { gchar buf[4096]; gsize br; gint count = 0; z_stream_set_nonblock(self->super.endpoints[EP_CLIENT], TRUE); while (count < 10 && z_stream_read(self->super.endpoints[EP_CLIENT], buf, sizeof(buf), &br, NULL) == G_IO_STATUS_NORMAL) { count++; } z_stream_set_nonblock(self->super.endpoints[EP_CLIENT], FALSE); } static ZPolicyObj * http_response_policy_get(HttpProxy *self) { gchar *response_keys[2]; response_keys[0] = self->request_method->str; response_keys[1] = self->response; return z_dim_hash_table_search(self->response_policy, 2, response_keys); } static gboolean http_filter_response(HttpProxy *self) { z_proxy_enter(self); if (self->proto_version[EP_SERVER] >= 0x0100) { ZPolicyObj *f, *res; gint rc; f = http_response_policy_get(self); if (f) { ZPolicyObj *handler; guint filter_type; gchar *errmsg; z_policy_lock(self->super.thread); if (!z_policy_tuple_get_verdict(f, &filter_type)) { /*LOG This message indicates that the response hash contains an invalid item for the given response. Check your Zorp configuration. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid response hash item; request='%s', response='%d'", self->request_method->str, self->response_code); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } z_policy_unlock(self->super.thread); g_string_sprintf(self->error_info, "Response %d for %s denied by policy.", self->response_code, self->request_method->str); switch (filter_type) { case HTTP_RSP_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(f, "(iO)", &filter_type, &handler)) { /*LOG This message indicates that the response hash contains an invalid POLICY tuple for the given response. It should contain a valid call-back function in the tuple. */ z_proxy_log(self, HTTP_POLICY, 1, "Error parsing HTTP_RSP_POLICY in response hash; request='%s', response='%d'", self->request_method->str, self->response_code); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } res = z_policy_call_object(handler, z_policy_var_build("(sssi)", self->request_method->str, self->request_url->str, self->request_version, self->response_code), self->super.session_id); if (!z_policy_var_parse(res, "i", &rc)) { g_string_assign(self->error_info, "Error in policy handler."); rc = HTTP_RSP_REJECT; } z_policy_var_unref(res); z_policy_unlock(self->super.thread); break; case HTTP_RSP_REJECT: errmsg = NULL; z_policy_lock(self->super.thread); if (!z_policy_var_parse_tuple(f, "i|s", &filter_type, &errmsg)) { /*LOG This message indicates that the response hash contains an invalid REJECT tuple for the given response. It should contain an error message, which is sent back to the client. */ z_proxy_log(self, HTTP_POLICY, 1, "Error parsing HTTP_RSP_REJECT in response hash; request='%s', response='%d'", self->request_method->str, self->response_code); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } z_policy_unlock(self->super.thread); if (errmsg) g_string_assign(self->error_info, errmsg); /* fallthrough */ case HTTP_RSP_ACCEPT: case HTTP_RSP_DENY: case HTTP_RSP_ABORT: /* dropped command */ rc = filter_type; break; default: /*LOG This message indicates that the response hash contains an invalid action for the given response. Check your Zorp configuration. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid response hash item; request='%s', response='%d'", self->request_method->str, self->response_code); z_proxy_return(self, FALSE); } switch (rc) { case HTTP_RSP_ACCEPT: break; default: case HTTP_RSP_REJECT: /*LOG This message indicates that the status code returned by the server is not a permitted response code for this request. */ z_proxy_log(self, HTTP_POLICY, 2, "Response not permitted by policy; req='%s', rsp='%d'", self->request_method->str, self->response_code); self->error_code = HTTP_MSG_POLICY_VIOLATION; z_proxy_return(self, FALSE); } } if (!http_filter_headers(self, EP_SERVER, NULL)) z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } static gboolean http_format_response(HttpProxy *self, gboolean stacked, GString *response) { if (self->proto_version[EP_SERVER] >= 0x0100) { if (!stacked) { http_flat_headers(&self->headers[EP_SERVER]); g_string_set_size(response, 4000); g_string_truncate(response, 0); g_string_append_const(response, "HTTP"); g_string_append_c(response, '/'); g_string_append_c(response, '0' + ((self->proto_version[EP_SERVER] & 0xFF00) >> 8)); g_string_append_c(response, '.'); g_string_append_c(response, '0' + (self->proto_version[EP_SERVER] & 0x00FF)); g_string_append_c(response, ' '); g_string_append(response, self->response); g_string_append_c(response, ' '); g_string_append_gstring(response, self->response_msg); g_string_append_const(response, "\r\n"); g_string_append_gstring(response, self->headers[EP_SERVER].flat); g_string_append_const(response, "\r\n"); } else { http_flat_headers_into(&self->headers[EP_SERVER], response); } } else g_string_truncate(response, 0); return TRUE; } static gboolean http_copy_response(HttpProxy *self) { gboolean suppress_data, expect_data; z_proxy_enter(self); /* self->connection_hdr must never be NULL for server->client direction when at least HTTP/1.0 is used */ if (self->proto_version[EP_SERVER] >= 0x0100) { g_assert(self->connection_hdr); if (self->connection_mode != self->server_connection_mode) self->connection_hdr->present = TRUE; if (self->connection_hdr->present) { g_string_assign(self->connection_hdr->name, self->request_type == HTTP_REQTYPE_SERVER ? "Connection" : "Proxy-Connection"); http_assign_connection_hdr_value(self, self->connection_hdr->value); } } expect_data = (self->response_flags & HTTP_RESP_FLG_DONTEXPECT) == 0; suppress_data = (self->response_flags & HTTP_RESP_FLG_SUPPRESS) != 0 || (self->request_flags & HTTP_REQ_FLG_HEAD) != 0 || ((self->request_flags & HTTP_REQ_FLG_CONNECT) != 0 && self->response_code == 200); if (!http_data_transfer(self, HTTP_TRANSFER_NORMAL, EP_SERVER, self->super.endpoints[EP_SERVER], EP_CLIENT, self->super.endpoints[EP_CLIENT], expect_data, suppress_data, http_format_response)) z_proxy_return(self, FALSE); /* http_data_transfer already logs */ z_proxy_return(self, TRUE); } static gboolean http_handle_connect(HttpProxy *self) { /* connect is enabled here */ guint i; ZPolicyObj *res; gboolean called; gchar *success, *remote_host; gchar *colon, *end; gint remote_host_len, remote_port; z_proxy_enter(self); self->connection_mode = HTTP_CONNECTION_CLOSE; colon = strchr(self->request_url->str, ':'); if (colon) { remote_host = self->request_url->str; remote_host_len = colon - remote_host; remote_port = strtoul(colon + 1, &end, 10); } else { self->error_code = HTTP_MSG_CLIENT_SYNTAX; g_string_sprintf(self->error_info, "Invalid CONNECT request."); /*LOG This message indicates that the received CONNECT request did not include a port number to connect to. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Missing port number in CONNECT request; url='%s'", self->request_url->str); z_proxy_return(self, FALSE); } if (http_parent_proxy_enabled(self)) { g_string_assign(self->remote_server, self->parent_proxy->str); self->remote_port = self->parent_proxy_port; } else { /* From buffer is longer than we want to copy. */ g_string_assign_len(self->remote_server, remote_host, remote_host_len); self->remote_port = remote_port; } if (!http_connect_server(self)) z_proxy_return(self, FALSE); if (http_parent_proxy_enabled(self)) { GString *request = g_string_sized_new(64); http_format_request(self, FALSE, request); if (http_write(self, EP_SERVER, request->str, request->len) != G_IO_STATUS_NORMAL) { g_string_free(request, TRUE); z_proxy_return(self, FALSE); } g_string_free(request, TRUE); if (!http_fetch_response(self)) z_proxy_return(self, FALSE); if (self->response_code != 200) { /*LOG This message indicates that the our parent proxy refused our CONNECT request. It is likely that the parent proxy does not permit CONNECT method. */ z_proxy_log(self, HTTP_ERROR, 1, "Parent proxy refused our CONNECT request; host='%.*s', port='%d'", remote_host_len, remote_host, remote_port); z_proxy_log(self, HTTP_DEBUG, 6, "Processing response and headers (CONNECT);"); if (!http_process_response(self) || !http_filter_response(self) || !http_copy_response(self)) { z_proxy_log(self, HTTP_ERROR, 2, "Error copying CONNECT response, or CONNECT response rejected by policy; request='%s', response='%d'", self->request_method->str, self->response_code); } z_proxy_return(self, FALSE); } } /*LOG This message reports that CONNECT method is in use, and CONNECT method was accepted by out parent proxy. */ z_proxy_log(self, HTTP_DEBUG, 6, "Starting connect method;"); if (!http_parent_proxy_enabled(self)) { success = "HTTP/1.0 200 Connection established\r\n\r\n"; if (http_write(self, EP_CLIENT, success, strlen(success)) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); /* hmm... I/O error */ } else { if (!http_process_response(self) || !http_filter_response(self) || !http_copy_response(self)) { z_proxy_log(self, HTTP_ERROR, 2, "Error copying CONNECT response, or CONNECT response rejected by policy; request='%s', response='%d'", self->request_method->str, self->response_code); z_proxy_return(self, FALSE); } } /* FIXME: flush already buffered data, before starting plug */ /* NOTE: the child proxy uses the Python variables session.client_stream * and session.server_stream as its streams (see bug: #10347) */ for (i = EP_CLIENT; i < EP_MAX; i++) { while (self->super.endpoints[i]->child) self->super.endpoints[i] = z_stream_pop(self->super.endpoints[i]); } z_policy_lock(self->super.thread); res = z_policy_call(self->super.handler, "connectMethod", z_policy_var_build("()"), &called, self->super.session_id); if (!res || res == z_policy_none) { /*LOG This message indicates that an internal error occurred, the connectMethod function did not return an integer. Please report this event to the Zorp QA team (at devel@balabit.com). */ z_proxy_log(self, HTTP_ERROR, 1, "Internal error, connectMethod is expected to return a proxy instance;"); self->error_code = HTTP_MSG_POLICY_SYNTAX; z_policy_var_unref(res); z_policy_unlock(self->super.thread); z_proxy_leave(self); return FALSE; } z_policy_var_unref(res); z_policy_unlock(self->super.thread); /* release, but don't close fds */ for (i = EP_CLIENT; i < EP_MAX; i++) { z_stream_unref(self->super.endpoints[i]); self->super.endpoints[i] = NULL; } z_proxy_return(self, TRUE); } static void http_main(ZProxy *s) { HttpProxy *self = Z_CAST(s, HttpProxy); gint rerequest_attempts; z_proxy_enter(self); self->request_count = 0; http_client_stream_init(self); /* loop for keep-alive */ while (1) { self->error_code = -1; /*LOG This message reports that Zorp is fetching the request and the headers from the client. */ z_proxy_log(self, HTTP_DEBUG, 6, "Fetching request and headers;"); /* fetch request */ if (!http_fetch_request(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_CLIENT_SYNTAX; goto exit_request_loop; } if (!z_proxy_loop_iteration(s)) break; /*LOG This message reports that Zorp is processing the fetched request and the headers. */ z_proxy_log(self, HTTP_DEBUG, 6, "processing request and headers;"); if (!http_process_request(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_CLIENT_SYNTAX; goto exit_request_loop; } if (self->max_keepalive_requests != 0 && self->request_count >= self->max_keepalive_requests - 1) { /*LOG This message reports that the maximum number of requests in a keep-alive loop is reached, and Zorp is closing after this request. Check the max_keepalive_request attribute. */ z_proxy_log(self, HTTP_POLICY, 3, "Maximum keep-alive request reached, setting connection mode to close; count='%d', max='%d'", self->request_count, self->max_keepalive_requests); self->connection_mode = HTTP_CONNECTION_CLOSE; } /*LOG This message reports that Zorp is filtering the processed request and the headers. */ z_proxy_log(self, HTTP_DEBUG, 6, "Filtering request and headers;"); if (!http_filter_request(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_POLICY_SYNTAX; goto exit_request_loop; } /*LOG This message indicates that Zorp is recechecking the HTTP request after possible changes performed by the policy layer. */ z_proxy_log(self, HTTP_DEBUG, 6, "Reprocessing filtered request;"); if (!http_process_filtered_request(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_CLIENT_SYNTAX; goto exit_request_loop; } if (self->server_protocol == HTTP_PROTO_HTTP) { gboolean retry; if ((self->request_flags & HTTP_REQ_FLG_CONNECT)) { if (http_handle_connect(self)) z_proxy_return(self); /* connect method was successful, we can now safely quit */ goto exit_request_loop; } rerequest_attempts = self->rerequest_attempts; while (1) { retry = FALSE; /*LOG This message reports that Zorp is sending the filtered request and headers, and copies the requests data to the server. */ z_proxy_log(self, HTTP_DEBUG, 6, "Sending request and headers, copying request data;"); if (!retry && !http_copy_request(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_CLIENT_SYNTAX; retry = TRUE; } /*LOG This message reports that Zorp is fetching the response and headers from the server. */ z_proxy_log(self, HTTP_DEBUG, 6, "Fetching response and headers;"); if (!retry && !http_fetch_response(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_SERVER_SYNTAX; retry = TRUE; } if (retry && rerequest_attempts > 1) { z_proxy_log(self, HTTP_ERROR, 3, "Server request failed, retrying; attempts='%d'", rerequest_attempts); self->force_reconnect = TRUE; self->error_code = -1; rerequest_attempts--; continue; } else if (retry) { goto exit_request_loop; } else { break; } } if (!z_proxy_loop_iteration(s)) goto exit_request_loop; /*LOG This message reports that Zorp is processing the fetched response and the headers. */ z_proxy_log(self, HTTP_DEBUG, 6, "Processing response and headers;"); if (!http_process_response(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_SERVER_SYNTAX; goto exit_request_loop; } /*LOG This message reports that Zorp is filtering the processed response and the headers. */ z_proxy_log(self, HTTP_DEBUG, 6, "Filtering response and headers;"); if (!http_filter_response(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_POLICY_SYNTAX; goto exit_request_loop; } /*LOG This message reports that Zorp is sending the filtered response and headers, and copies the response data to the client. */ z_proxy_log(self, HTTP_DEBUG, 6, "Copying response and headers, copying response data;"); if (!http_copy_response(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_SERVER_SYNTAX; goto exit_request_loop; } } else if (self->server_protocol == HTTP_PROTO_FTP) { if (!http_handle_ftp_request(self)) { if (self->error_code == -1) self->error_code = HTTP_MSG_FTP_ERROR; } self->connection_mode = HTTP_CONNECTION_CLOSE; } else { /*LOG This message indicates an internal error in HTTP proxy. Please report this event to the Zorp QA team (at devel@balabit.com). */ z_proxy_log(self, CORE_ERROR, 1, "Internal error, invalid server_protocol; server_protocol='%d'", self->server_protocol); } if (self->connection_mode == HTTP_CONNECTION_CLOSE) goto exit_request_loop; if (self->server_connection_mode == HTTP_CONNECTION_CLOSE) { /* close the server connection, but keep the client connection in-tact */ self->force_reconnect = TRUE; } self->request_count++; /* NOTE: * In keepalive mode we have to disable authentication after the first round. */ if (self->auth) { z_policy_lock(self->super.thread); z_policy_var_unref(self->auth); self->auth = NULL; z_policy_unlock(self->super.thread); } } exit_request_loop: /*LOG This message reports that Zorp is exiting the keep-alive loop and closing the connection. */ z_proxy_log(self, HTTP_DEBUG, 6, "exiting keep-alive loop;"); if (self->error_code > 0) http_error_message(self, self->error_status, self->error_code, self->error_info); /* in some cases the client might still have some data already queued to us, * fetch and ignore that data to avoid the RST sent in these cases */ http_fetch_buffered_data(self); if (self->error_code <= 0 && self->reset_on_close) { int fd; fd = z_stream_get_fd(self->super.endpoints[EP_CLIENT]); if (fd >= 0) { struct linger l; l.l_onoff = 1; l.l_linger = 0; setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); } /* avoid z_stream_shutdown in the destroy path */ z_stream_close(self->super.endpoints[EP_CLIENT], NULL); z_stream_unref(self->super.endpoints[EP_CLIENT]); self->super.endpoints[EP_CLIENT] = NULL; } z_proxy_return(self); } static gboolean http_config(ZProxy *s) { HttpProxy *self = Z_CAST(s, HttpProxy); http_config_set_defaults(self); http_register_vars(self); if (Z_SUPER(s, ZProxy)->config(s)) { http_config_init(self); z_proxy_ssl_set_force_connect_at_handshake(s, TRUE); return TRUE; } return FALSE; } static void http_proxy_free(ZObject *s) { HttpProxy *self = Z_CAST(s, HttpProxy); guint i; z_enter(); for (i = EP_CLIENT; i < EP_MAX; i++) http_destroy_headers(&self->headers[i]); if (self->request_data) z_blob_unref(self->request_data); g_string_free(self->old_auth_header, TRUE); g_string_free(self->auth_header_value, TRUE); g_string_free(self->response_msg, TRUE); g_string_free(self->connected_server, TRUE); g_string_free(self->remote_server, TRUE); g_string_free(self->request_url, TRUE); http_destroy_url(&self->request_url_parts); /* NOTE: hashes are freed up by pyvars */ z_poll_unref(self->poll); z_proxy_free_method(s); z_return(); } static ZProxy * http_proxy_new(ZProxyParams *params) { HttpProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(HttpProxy), params), HttpProxy); z_return((&self->super)); } static void http_proxy_free(ZObject *s); ZProxyFuncs http_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), http_proxy_free, }, .config = http_config, .main = http_main, NULL }; Z_CLASS_DEF(HttpProxy, ZProxy, http_proxy_funcs); gint zorp_module_init(void) { http_proto_init(); auth_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); auth_mutex = g_mutex_new(); z_registry_add("http", ZR_PROXY, http_proxy_new); return TRUE; } zorp-3.9.5/modules/http/http.h000066400000000000000000000422271172670260400163150ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_HTTP_H_INCLUDED #define ZORP_MODULES_HTTP_H_INCLUDED #include #include #include #include #include #include #include #include #include /* general limits applied to headers, etc. */ #define HTTP_MAX_LINE 32768 #define HTTP_MAX_URL 32768 #define HTTP_BLOCKSIZE 4096 #define HTTP_MAX_EMPTY_REQUESTS 3 /* error tags */ #define HTTP_DEBUG "http.debug" #define HTTP_ERROR "http.error" #define HTTP_POLICY "http.policy" #define HTTP_REQUEST "http.request" #define HTTP_RESPONSE "http.response" #define HTTP_VIOLATION "http.violation" #define HTTP_ACCOUNTING "http.accounting" /* request specific constants */ #define HTTP_REQ_ACCEPT 1 #define HTTP_REQ_DENY 2 #define HTTP_REQ_REJECT 3 #define HTTP_REQ_ABORT 4 #define HTTP_REQ_POLICY 6 /* response specific constants */ #define HTTP_RSP_ACCEPT 1 #define HTTP_RSP_DENY 2 #define HTTP_RSP_REJECT 3 #define HTTP_RSP_ABORT 4 #define HTTP_RSP_POLICY 6 /* header specific constants */ #define HTTP_HDR_ACCEPT 1 #define HTTP_HDR_ABORT 4 #define HTTP_HDR_DROP 5 #define HTTP_HDR_POLICY 6 #define HTTP_HDR_CHANGE_NAME 100 #define HTTP_HDR_CHANGE_VALUE 101 #define HTTP_HDR_CHANGE_BOTH 102 #define HTTP_HDR_CHANGE_REGEXP 103 #define HTTP_HDR_INSERT 104 #define HTTP_HDR_REPLACE 105 #define HTTP_STK_NONE 1 #define HTTP_STK_DATA 2 #define HTTP_STK_MIME 3 /* connection mode */ #define HTTP_CONNECTION_CLOSE 0 #define HTTP_CONNECTION_KEEPALIVE 1 #define HTTP_CONNECTION_UNKNOWN 2 /* request type */ #define HTTP_REQTYPE_SERVER 0 /* simple server request */ #define HTTP_REQTYPE_PROXY 1 /* proxy request */ /* request protocol flags */ #define HTTP_REQ_FLG_HEAD 2 #define HTTP_REQ_FLG_ASTERIX 4 #define HTTP_REQ_FLG_CONNECT 8 /* response protocol flags */ #define HTTP_RESP_FLG_CONTINUE 1 #define HTTP_RESP_FLG_SUPPRESS 2 #define HTTP_RESP_FLG_DONTEXPECT 4 #define HTTP_RESP_FLG_STOP 8 /* HTTP header value flags */ /* format flags */ #define HTTP_HDR_FF_URL (1 << 1) #define HTTP_HDR_FF_ANY (1 << 2) /* character flags */ #define HTTP_HDR_CF_ALPHA (1 << 3) #define HTTP_HDR_CF_NUMERIC (1 << 4) #define HTTP_HDR_CF_SPACE (1 << 5) #define HTTP_HDR_CF_COMMA (1 << 6) #define HTTP_HDR_CF_DOT (1 << 7) #define HTTP_HDR_CF_BRACKET (1 << 8) #define HTTP_HDR_CF_EQUAL (1 << 9) #define HTTP_HDR_CF_DASH (1 << 10) #define HTTP_HDR_CF_SLASH (1 << 11) #define HTTP_HDR_CF_COLON (1 << 12) #define HTTP_HDR_CF_SEMICOLON (1 << 13) #define HTTP_HDR_CF_AT (1 << 14) #define HTTP_HDR_CF_UNDERLINE (1 << 15) #define HTTP_HDR_CF_AND (1 << 16) #define HTTP_HDR_CF_BACKSLASH (1 << 17) // * #define HTTP_HDR_CF_ASTERIX (1 << 19) #define HTTP_HDR_CF_DOLLAR (1 << 20) // # #define HTTP_HDR_CF_HASHMARK (1 << 21) #define HTTP_HDR_CF_PLUS (1 << 22) #define HTTP_HDR_CF_QUOTE (1 << 23) #define HTTP_HDR_CF_QUESTIONMARK (1 << 24) #define HTTP_HDR_CF_PERCENT (1 << 25) #define HTTP_HDR_CF_TILDE (1 << 26) #define HTTP_HDR_CF_EXCLAM (1 << 27) /* error codes */ #define HTTP_MSG_OK 0 #define HTTP_MSG_CLIENT_SYNTAX 1 #define HTTP_MSG_SERVER_SYNTAX 2 #define HTTP_MSG_POLICY_SYNTAX 3 #define HTTP_MSG_POLICY_VIOLATION 4 #define HTTP_MSG_INVALID_URL 5 #define HTTP_MSG_CONNECT_ERROR 6 #define HTTP_MSG_IO_ERROR 7 #define HTTP_MSG_AUTH_REQUIRED 8 #define HTTP_MSG_CLIENT_TIMEOUT 9 #define HTTP_MSG_SERVER_TIMEOUT 10 #define HTTP_MSG_BAD_CONTENT 11 #define HTTP_MSG_FTP_ERROR 12 #define HTTP_MSG_REDIRECT 13 /* protocol to pull data on the server side */ #define HTTP_PROTO_HTTP 0 #define HTTP_PROTO_FTP 1 /* special HTTP lengths, values >= 0 are the exact length of the chunk */ #define HTTP_LENGTH_NONE -2 #define HTTP_LENGTH_UNKNOWN -1 #define HTTP_TRANSFER_NORMAL 0 #define HTTP_TRANSFER_TO_BLOB 1 #define HTTP_TRANSFER_FROM_BLOB 2 typedef struct _HttpProxy HttpProxy; typedef struct _HttpTransfer HttpTransfer; typedef struct _HttpHeader HttpHeader; typedef struct _HttpHeaders HttpHeaders; typedef struct _HttpURL HttpURL; typedef gboolean (*HttpTransferPreambleFunc)(HttpProxy *self, gboolean stacked, GString *preamble); /* this structure represents an HTTP header, it can easily be added to our * header structure without having to also include it in the protocol when * reconstructing the list of headers by using the 'present' member field. * When present is TRUE the header will be sent to the peers, when it is * FALSE it will not. */ struct _HttpHeader { GString *name; GString *value; gboolean present; }; /* This structure represents a set of headers with possibility to quickly * look headers up (through a GHashTable indexed by the header name), and also * retain original order, using a linked list. */ struct _HttpHeaders { /* linked list of HttpHeader structures */ GList *list; /* hash table for quick lookups */ GHashTable *hash; /* flattened representation of the headers */ GString *flat; }; struct _HttpURL { /* this is the original form of the URL local part in case canonicalization was disabled */ GString *original_local; /* all gstrings below might contain NUL characters as they store the URL-decoded form */ GString *scheme; GString *user; GString *passwd; GString *host; guint port; GString *file; GString *query; GString *fragment; gboolean need_brackets; /* IPv6 addresses are surrounded by brackets, this information needs to be preserved for formatting */ }; typedef struct _HttpElementInfo { gchar *name; guint32 flags; gssize max_len; /* only used for headers */ } HttpElementInfo; /* This structure represents an HTTP proxy */ struct _HttpProxy { ZProxy super; /* poll is used during transfers */ ZPoll *poll; /* stacked proxy */ ZStackedProxy *stacked; /* general I/O timeout */ guint timeout; /* timeout we wait for a request */ guint timeout_request; /* timeout we wait for a response */ guint timeout_response; guint rerequest_attempts; gboolean request_data_stored; ZBlob *request_data; /* request/response header-sets */ HttpHeaders headers[EP_MAX]; /* maximum number of headers in a single request/response */ guint max_header_lines; /* these values can be used to change the actual header name/value while * iterating through the set of headers */ GString *current_header_name, *current_header_value; /* borrowed reference to the request or response connection header * modifyable as long as the request/response headers are not * reconstructed */ HttpHeader *connection_hdr; /* inband authentication provider */ ZAuthProvider *auth; /* dummy, exported variable to indicate that we are able to deal with inband authentication */ gboolean auth_inband_supported; /* whether to forward authentication requests */ gboolean auth_forward; /* authentication realm to show to our clients */ GString *auth_realm; /* the value in the authentication header, used when forwarding * credentials (see auth_forward) */ GString *auth_header_value; /* cached authentication value, we do not authenticate persistent * requests, provided they have the same authentication header */ GString *old_auth_header; /* request method, like GET or POST */ GString *request_method; /* request flags (one of HTTP_REQ_FLG_*) */ guint request_flags; /* request url as sent by the client */ GString *request_url; HttpURL request_url_parts; /* HTTP version as presented in the client request */ gchar request_version[16]; /* proxy or server type request was received, HTTP_REQ_PROXY or HTTP_REQ_SERVER */ guint request_type; /* the protocol used to retrieve data HTTP/FTP */ guint server_protocol; /* port range permitted in non-transparent mode */ GString *target_port_range; /* server we are connected to, a new connection is established once this * changes */ GString *connected_server; /* port we are connected to */ guint connected_port; /* the target server as derived from the request (URL and Host header) */ GString *remote_server; /* the target port as dervied from the request */ guint remote_port; gboolean use_default_port_in_transparent_mode; /* whether to use canonicalized URLs by default */ gboolean use_canonicalized_urls; /* specifies the default HTTP port, which is used when the port is not * specified in the URL */ guint default_http_port; /* specifies the default FTP port, which is used when the port is not * specified in the URL */ guint default_ftp_port; /* HTTP version as presented in the server's response */ gchar response_version[16]; /* response status code, represented by 3 digits and a trailing NUL character */ gchar response[4]; /* response flags, HTTP_RESP_FLG_* */ guint response_flags; /* parsed representation of response */ gint response_code; /* response message at the tail of the HTTP status line */ GString *response_msg; /* client connection persistency, one of HTTP_CONNECTION_* */ guint connection_mode; guint server_connection_mode; gboolean keep_persistent; /* whether we are transparent, e.g. leave request type intact */ gboolean transparent_mode; /* whether to allow incoming server requests */ gboolean permit_server_requests; /* whether to allow incoming proxy requests */ gboolean permit_proxy_requests; /* whether to allow %uXXXX encoding in URLs */ gboolean permit_unicode_url; /* whether to care about hexadecimal encoded characters validity */ gboolean permit_invalid_hex_escape; /* whether to permit HTTP/0.9 responses at all */ gboolean permit_http09_responses; /* whether to permit both Proxy-Connection and Connection headers in requests */ gboolean permit_both_connection_headers; /* whether to permit FTP requests over non-transparent HTTP */ gboolean permit_ftp_over_http; /* FTP over HTTP variables */ ZAttach *ftp_data_attach; /* address, or hostname of the parent proxy, if empty direct connection is used */ GString *parent_proxy; /* port of the parent proxy */ guint parent_proxy_port; /* rewrite host header when redirection is done */ gboolean rewrite_host_header; gboolean reset_on_close; /* require the existance of the Host: header */ gboolean require_host_header; /* permit responses with no terminating CRLF and data */ gboolean permit_null_response; /* 0: accept rfc incompliant headers, 1: require rfc compliance */ gboolean strict_header_checking; guint strict_header_checking_action; /* parsed protocol version: 0x major minor (0x0009 0x0100 0x0101) */ guint proto_version[EP_MAX]; /* user tunable protocol limits */ guint max_line_length; guint max_hostname_length; guint max_url_length; guint max_keepalive_requests; guint max_body_length; guint max_chunk_length; /* number of requests so far */ guint request_count; /* policy hash to process on request methods */ GHashTable *request_method_policy; /* policy hash to process on request headers */ GHashTable *request_header_policy; /* policy hash to process on response codes */ ZDimHashTable *response_policy; /* policy hash to process on response headers */ GHashTable *response_header_policy; /* hack: when transfer feels the connection to the server should be * reestablished, it sets this value to TRUE */ gboolean reattempt_connection; gboolean force_reconnect; /* transfer object */ HttpTransfer *transfer; /* buffer size used while copying the blobs */ guint buffer_size; /* information used when generating the built in error pages */ /* internal error codes, HTTP_MSG_* */ gint error_code; /* status code to send to the client (e.g. 500 */ guint error_status; /* whether to generate error pages at all, or simply return an empty page */ gboolean error_silent; /* additional information to be shown to the client (included in the error pages as @INFO@) */ GString *error_info; /* error message sent on the HTTP status line */ GString *error_msg; /* headers sent together with the error page */ GString *error_headers; /* the directory where error file templates are stored */ GString *error_files_directory; /* Maximum allowed time between two forced authentication request */ gint max_auth_time; /* Enable authentication cache based on Cookies. */ gboolean auth_by_cookie; /* Do not authenticate this amount of sec. */ gint auth_cache_time; /* Update the auth cache stamp at each request */ gboolean auth_cache_update; /* Categories the request falls into */ ZPolicyObj *request_categories; GString *append_cookie; }; extern ZClass HttpProxy__class; typedef guint (*HttpHeaderFilter)(HttpProxy *self, GString *header_name, GString *header_value); guint http_write(HttpProxy *self, guint side, gchar *buf, size_t buflen); gboolean http_connect_server(HttpProxy *self); gboolean http_data_transfer(HttpProxy *self, gint transfer_type, guint from, ZStream *from_stream, guint to, ZStream *to_stream, gboolean expect_data, gboolean suppress_data, HttpTransferPreambleFunc format_preamble); gboolean http_lookup_header(HttpHeaders *headers, gchar *what, HttpHeader **p); GHashTable *http_parse_header_cookie(HttpHeaders *hdrs); gboolean http_fetch_headers(HttpProxy *self, int side); gboolean http_filter_headers(HttpProxy *self, guint side, HttpHeaderFilter filter); gboolean http_flat_headers(HttpHeaders *hdrs); gboolean http_flat_headers_into(HttpHeaders *hdrs, GString *into); void http_clear_headers(HttpHeaders *hdrs); void http_init_headers(HttpHeaders *hdrs); void http_destroy_headers(HttpHeaders *hdrs); HttpHeader * http_add_header(HttpHeaders *hdrs, gchar *name, gint name_len, gchar *value, gint value_len); void http_log_headers(HttpProxy *self, gint side, gchar *tag); gint http_filter_hash_compare(gconstpointer a, gconstpointer b); gint http_filter_hash_bucket(gconstpointer a); /* URL processing */ gboolean http_parse_url(HttpURL *url, gboolean permit_unicode_url, gboolean permit_invalid_hex_escape, gboolean permit_relative_url, gchar *url_str, const gchar **reason); gboolean http_format_url(HttpURL *url, GString *encode_buf, gboolean format_absolute, gboolean permit_unicode_url, gboolean canonicalized, const gchar **reason); void http_init_url(HttpURL *url); void http_destroy_url(HttpURL *url); /* request/response processing */ gboolean http_split_request(HttpProxy *self, gchar *line, gint length); gboolean http_split_response(HttpProxy *self, gchar *line, gint line_length); gboolean http_parse_version(HttpProxy *self, gint side, gchar *version_str); gboolean http_handle_ftp_request(HttpProxy *self); void http_proto_init(void); /* inline functions */ extern GHashTable *request_proto_hash; extern GHashTable *response_proto_hash; extern GHashTable *request_hdr_proto_hash; extern GHashTable *response_hdr_proto_hash; static inline guint http_proto_lookup_hash(GHashTable *hash, const gchar *index) { HttpElementInfo *e; e = g_hash_table_lookup(hash, index); if (e) return e->flags; return 0; } static inline guint http_proto_request_lookup(const gchar *req) { return http_proto_lookup_hash(request_proto_hash, req); } static inline guint http_proto_response_lookup(const gchar *resp) { return http_proto_lookup_hash(response_proto_hash, resp); } static inline HttpElementInfo * http_proto_request_hdr_lookup(const gchar *req) { return g_hash_table_lookup(request_hdr_proto_hash, req); } static inline HttpElementInfo * http_proto_response_hdr_lookup(const gchar *resp) { return g_hash_table_lookup(response_hdr_proto_hash, resp); } #endif zorp-3.9.5/modules/http/httpfltr.c000066400000000000000000001275041172670260400172020ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "http.h" #include #include #include #include #include /* transfer states */ #define HTTP_SR_INITIAL 0 #define HTTP_SR_PUSH_HEADERS 10 #define HTTP_SR_PUSH_HEADERS_MAX 10 #define HTTP_SR_READ_INITIAL 20 #define HTTP_SR_READ_CHUNK_LENGTH 21 #define HTTP_SR_READ_CHUNK 22 #define HTTP_SR_READ_FOOTER 23 #define HTTP_SR_READ_ENTITY 24 /* reading content-length or EOF terminated entity */ #define HTTP_DW_INITIAL 0 #define HTTP_DW_POP_HEADERS 10 #define HTTP_DW_POP_HEADERS_CR 11 #define HTTP_DW_POP_HEADERS_LF 12 #define HTTP_DW_POP_HEADERS_MAX 12 #define HTTP_DW_WRITE_PREAMBLE 20 #define HTTP_DW_WRITE_INITIAL 21 #define HTTP_DW_FORMAT_CHUNK_LENGTH 22 #define HTTP_DW_WRITE_CHUNK_LENGTH 23 #define HTTP_DW_WRITE_CHUNK 24 #define HTTP_DW_WRITE_FOOTER 25 struct _HttpTransfer { ZTransfer2 super; GString *preamble; guint preamble_ofs; /* whether to actually suppress DATA even if they seem to be some (e.g. response HEAD request) */ gboolean suppress_data; /* whether to expect data if there is nothing explicit indicating it (e.g. response entities) */ gboolean expect_data; /* transfer endpoints */ gint transfer_from, transfer_to; gint transfer_type; /* the headers to send to the downstream proxy */ GString *stacked_preamble; /* offset within mime_headers if writing blocked */ guint stacked_preamble_ofs; guint stacked_preamble_read_bytes; /* function used to format the preamble to stacked proxy/peer */ HttpTransferPreambleFunc format_preamble_func; /* whether to push mime headers to the downstream proxy */ gboolean push_mime_headers; /* whether to force the end of the connection */ gboolean force_nonpersistent_mode; /* we can stay persisent, but only if we receive a content-length hint from downstream proxy */ gboolean persistent_with_cl_hint_only; HttpHeader *transfer_encoding_hdr, *content_length_hdr; /* used while stripping off MIME headers returned by the downstream proxy */ gint dst_eol_count; /* complete content_length, -2 if no entity, -1 if length unknown, otherwise the exact length */ gint64 content_length; /* indicates whether source is chunked */ gboolean src_chunked; /* source read state */ guint src_read_state; /* indicates that the current chunk is an EOF chunk */ gboolean src_last_chunk; /* indicates that this is the last chunk and that it was truncated * because the body was over max_body_length */ gboolean src_chunk_truncated; /* the number of bytes waiting to be read in the current chunk */ guint64 src_chunk_left; /* the total number of bytes read during this transfer */ guint64 src_whole_length; gboolean dst_chunked; guint dst_write_state; /* the number of bytes still to be written to the destination */ guint64 dst_chunk_left; gchar dst_chunk_length_buf[32]; guint dst_chunk_length_ofs; guint dst_chunk_length_end; guint dst_footer_ofs; /* the total number of bytes in the chunk being written */ guint64 dst_chunk_length; /* the total number of transferred bytes on the write side */ guint64 dst_whole_length; }; extern ZClass HttpTransfer__class; /* HttpTransfer implementation */ static GIOStatus http_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize count, gsize *bytes_read, GError **err) { HttpTransfer *self = Z_CAST(s, HttpTransfer); HttpProxy *owner = (HttpProxy *) s->owner; GError *local_error = NULL; gsize br; GIOStatus res = G_IO_STATUS_NORMAL; if (self->src_read_state == HTTP_SR_INITIAL) self->src_read_state = HTTP_SR_PUSH_HEADERS; if (self->src_read_state >= HTTP_SR_PUSH_HEADERS && self->src_read_state <= HTTP_SR_PUSH_HEADERS_MAX) { if (self->push_mime_headers && self->stacked_preamble->len > 0) { gint move; *bytes_read = 0; move = MIN(count, self->stacked_preamble->len - self->stacked_preamble_ofs); memmove(buf, self->stacked_preamble->str + self->stacked_preamble_ofs, move); self->stacked_preamble_ofs += move; *bytes_read = move; if (self->stacked_preamble_ofs == self->stacked_preamble->len) { z_transfer2_set_proxy_out(s, FALSE); self->src_read_state = HTTP_SR_READ_INITIAL; } return G_IO_STATUS_NORMAL; } else { self->src_read_state = HTTP_SR_READ_INITIAL; } } *bytes_read = 0; if (self->src_chunked) { /* read as a chunked stream */ switch (self->src_read_state) { case HTTP_SR_READ_INITIAL: self->src_whole_length = 0; self->src_read_state = HTTP_SR_READ_CHUNK_LENGTH; z_stream_line_set_poll_partial(stream, FALSE); /* fallthrough */ case HTTP_SR_READ_CHUNK_LENGTH: { gchar line_buf[32], *line; gsize line_length; guint32 chunk_length; gchar *end; res = z_stream_line_get(stream, &line, &line_length, NULL); if (res == G_IO_STATUS_NORMAL) { /* a complete line was read, check if it is a valid chunk length */ if (line_length >= sizeof(line_buf) - 1) { /*LOG This message indicates that the chunk length line is too long. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Chunk length line too long; line='%.*s'", (gint) line_length, line); res = G_IO_STATUS_ERROR; break; } /* we already checked that line_buf is large enough */ memcpy(line_buf, line, line_length); line_buf[line_length] = 0; chunk_length = strtoul(line_buf, &end, 16); if (end == line_buf) { /* hmm... invalid chunk length */ /*LOG This message indicates that the chunk length is invalid. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Invalid chunk length; line='%s'", line_buf); res = G_IO_STATUS_ERROR; break; } /* * NOTE: the string pointed by end is NUL terminated, thus * we will not overflow our buffer */ while (*end == ' ') end++; if (*end == ';') { /* ignore and strip chunk extensions */ *end = 0; } if (*end) { /* hmm... invalid chunk length */ /*LOG This message indicates that the chunk length is invalid. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Invalid chunk length; line='%s'", line_buf); res = G_IO_STATUS_ERROR; break; } if ((owner->max_chunk_length && chunk_length > owner->max_chunk_length) || (chunk_length & 0x80000000)) { /*LOG This message indicates that the length of the chunk is larger than allowed or is a negative number. Check the 'max_chunk_length' attribute. */ z_proxy_log(self->super.owner, HTTP_POLICY, 2, "Chunk too large; length='%d', max_chunk_length='%d'", chunk_length, owner->max_chunk_length); res = G_IO_STATUS_ERROR; break; } if (owner->max_body_length && (guint) self->src_whole_length + chunk_length > owner->max_body_length) { /* this chunk would be over body_length limit */ chunk_length = owner->max_body_length - self->src_whole_length; self->src_chunk_left = chunk_length; self->force_nonpersistent_mode = TRUE; self->src_chunk_truncated = TRUE; } self->src_chunk_left = chunk_length; self->src_last_chunk = chunk_length == 0; self->src_read_state = HTTP_SR_READ_CHUNK; z_stream_line_set_poll_partial(stream, TRUE); /* fall through */ } else break; } case HTTP_SR_READ_CHUNK: if (!self->src_last_chunk) { res = z_stream_read(stream, buf, MIN(self->src_chunk_left, count), &br, &local_error); if (res == G_IO_STATUS_NORMAL) { self->src_whole_length += br; self->src_chunk_left -= br; *bytes_read = br; } else if (res == G_IO_STATUS_EOF) { /* unexpected eof */ /*LOG This message indicates that Zorp unexpectedly got EOF during chunk encoded data transfer. It is likely a caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Unexpected EOF while dechunking stream;"); res = G_IO_STATUS_ERROR; break; } if (self->src_chunk_left == 0) { self->src_read_state = HTTP_SR_READ_FOOTER; z_stream_line_set_poll_partial(stream, FALSE); } break; } else { self->src_read_state = HTTP_SR_READ_FOOTER; z_stream_line_set_poll_partial(stream, FALSE); /* fallthrough */ } case HTTP_SR_READ_FOOTER: { gchar *line; gsize line_length; if (!self->src_chunk_truncated) { res = z_stream_line_get(stream, &line, &line_length, NULL); } else { res = G_IO_STATUS_EOF; } if (res == G_IO_STATUS_NORMAL) { if (line_length != 0) { /*LOG This message indicates that the chunk footer contains data. It is likely caused by a buggy client or server. */ z_proxy_log(self->super.owner, HTTP_VIOLATION, 1, "Chunk footer is not an empty line;"); res = G_IO_STATUS_ERROR; break; } if (self->src_last_chunk) { res = G_IO_STATUS_EOF; } else { self->src_read_state = HTTP_SR_READ_CHUNK_LENGTH; z_stream_line_set_poll_partial(stream, TRUE); /* come back later */ res = G_IO_STATUS_AGAIN; } break; } break; } } } else { /* copy until EOF or self->content_length bytes */ if (self->content_length == HTTP_LENGTH_NONE) { res = G_IO_STATUS_EOF; } else { if (self->src_read_state == HTTP_SR_INITIAL) { self->src_whole_length = 0; self->src_read_state = HTTP_SR_READ_ENTITY; } if (self->content_length == HTTP_LENGTH_UNKNOWN) { if (owner->max_body_length && self->src_whole_length + count >= owner->max_body_length) { count = owner->max_body_length - self->src_whole_length; } if (count == 0) { self->force_nonpersistent_mode = TRUE; res = G_IO_STATUS_EOF; } else res = z_stream_read(stream, buf, count, &br, &local_error); } else { /* for specified content-length, max_body_length has already been processed, and content_length contains the number of bytes to be transferred, but maximum max_body_length */ if (self->content_length >= 0 && (guint64) self->content_length == self->src_whole_length) res = G_IO_STATUS_EOF; else res = z_stream_read(stream, buf, MIN(count, self->content_length - self->src_whole_length), &br, &local_error); } if (res == G_IO_STATUS_NORMAL) { self->src_whole_length += br; *bytes_read = br; } } } if (local_error) g_propagate_error(err, local_error); return res; } static GIOStatus http_transfer_src_shutdown(ZTransfer2 *self G_GNUC_UNUSED, ZStream *s G_GNUC_UNUSED, GError **err G_GNUC_UNUSED) { /* do nothing */ return G_IO_STATUS_NORMAL; } static GIOStatus http_transfer_dst_write_preamble(HttpTransfer *self, ZStream *stream, GError **err) { GIOStatus res = G_IO_STATUS_NORMAL; GError *local_error = NULL; gsize bw; http_log_headers((HttpProxy *) self->super.owner, self->transfer_from, "postfilter"); res = z_stream_write(stream, &self->preamble->str[self->preamble_ofs], self->preamble->len - self->preamble_ofs, &bw, &local_error); if (res == G_IO_STATUS_NORMAL) { self->preamble_ofs += bw; if (self->preamble_ofs != self->preamble->len) { res = G_IO_STATUS_AGAIN; } } else if (self->src_read_state == HTTP_SR_INITIAL && g_error_matches(local_error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_PIPE)) { /* we can only reattempt a connection if nothing was read from the * client, that's the reason behind src_read_state == INITIAL above */ /* FIXME: this is a hack. A better solution would be to propagate * GError to the proxy so this case can be handled there cleanly */ ((HttpProxy *) self->super.owner)->reattempt_connection = TRUE; } if (local_error) g_propagate_error(err, local_error); return res; } /* NOTE: this function assumes that ZTransfer2 does not change the buffer * between successive calls when an G_IO_STATUS_AGAIN is returned. This is * required for performance reasons, as otherwise this function would have * to copy the buffer contents to a private buffer and return * G_IO_STATUS_AGAIN while it is being flushed.*/ static GIOStatus http_transfer_dst_write(ZTransfer2 *s, ZStream *stream, const gchar *buf, gsize count, gsize *bytes_written, GError **err) { HttpTransfer *self = Z_CAST(s, HttpTransfer); HttpProxy *owner = (HttpProxy *) self->super.owner; GError *local_error = NULL; gsize bw; GIOStatus res = G_IO_STATUS_NORMAL; *bytes_written = 0; if (self->dst_write_state == HTTP_DW_INITIAL) self->dst_write_state = HTTP_DW_POP_HEADERS; if (self->dst_write_state >= HTTP_DW_POP_HEADERS && self->dst_write_state <= HTTP_DW_POP_HEADERS_MAX) { if (self->push_mime_headers) { gsize i; for (i = 0; i < count; i++) { switch (self->dst_write_state) { case HTTP_DW_POP_HEADERS: switch (buf[i]) { case '\r': self->dst_write_state = HTTP_DW_POP_HEADERS_CR; break; case '\n': self->dst_write_state = HTTP_DW_POP_HEADERS_LF; break; default: break; } break; case HTTP_DW_POP_HEADERS_CR: switch (buf[i]) { case '\n': self->dst_write_state = HTTP_DW_POP_HEADERS_LF; break; default: self->dst_write_state = HTTP_DW_POP_HEADERS; break; } break; } if (self->dst_write_state == HTTP_DW_POP_HEADERS_LF) { /* new line found */ self->dst_eol_count++; if (self->dst_eol_count == 2) { /* end of headers */ i++; self->dst_write_state = HTTP_DW_WRITE_PREAMBLE; break; } else { self->dst_write_state = HTTP_DW_POP_HEADERS; } } else if (self->dst_write_state == HTTP_DW_POP_HEADERS) self->dst_eol_count = 0; } /* at this point we have either processed the whole buffer (i == * count), or a small chunk of data is at the tail of our headers * to be ignored. In either case return G_IO_STATUS_NORMAL and the * number of processed bytes. */ *bytes_written = i; self->stacked_preamble_read_bytes += i; return G_IO_STATUS_NORMAL; } else self->dst_write_state = HTTP_DW_WRITE_PREAMBLE; } if (self->dst_write_state == HTTP_DW_WRITE_PREAMBLE) { gboolean reformat_preamble = FALSE; if (self->super.child_content_length_hint_set) { /* we stay in persistent mode if possible */ /*LOG This message reports that the stacked proxy sent a content-length hint on how much data will be sent and the http proxy is using it to set the Content-Length header. */ z_proxy_log(owner, HTTP_DEBUG, 6, "Stacked proxy sent a content-length hint, using it; expected_content_length='%" G_GINT64_FORMAT "'", self->super.child_content_length_hint); g_string_sprintf(self->content_length_hdr->value, "%" G_GINT64_FORMAT, self->super.child_content_length_hint - self->stacked_preamble_read_bytes); self->content_length_hdr->present = TRUE; self->transfer_encoding_hdr->present = FALSE; self->dst_chunked = FALSE; reformat_preamble = TRUE; } else if (self->persistent_with_cl_hint_only) { /* when we require content-length hint to remain persistent and we have no content-length hint, we force ourselves to non-persistent mode */ self->force_nonpersistent_mode = TRUE; self->content_length_hdr->present = FALSE; reformat_preamble = TRUE; } if (self->force_nonpersistent_mode && owner->connection_mode == HTTP_CONNECTION_KEEPALIVE) { owner->connection_mode = owner->server_connection_mode = HTTP_CONNECTION_CLOSE; /* we must change our connection header right here as our core has * already made its decision, but we must override that */ if (owner->connection_hdr) { g_string_assign(owner->connection_hdr->value, "close"); reformat_preamble = TRUE; } } if (reformat_preamble) self->format_preamble_func(owner, FALSE, self->preamble); /* take care about the preamble (request/response itself) */ res = http_transfer_dst_write_preamble(self, stream, &local_error); if (res != G_IO_STATUS_NORMAL) { goto propagate_exit; } self->dst_write_state = HTTP_DW_WRITE_INITIAL; } /* ok, now take care about the data, and possibly enchunk it on the way */ if (self->dst_chunked) { switch (self->dst_write_state) { case HTTP_DW_WRITE_INITIAL: self->dst_write_state = HTTP_DW_FORMAT_CHUNK_LENGTH; self->dst_whole_length = 0; /* fallthrough */ case HTTP_DW_FORMAT_CHUNK_LENGTH: { if (count == 0) { res = G_IO_STATUS_NORMAL; break; } self->dst_chunk_length = 0; self->dst_chunk_left = count; g_snprintf(self->dst_chunk_length_buf, sizeof(self->dst_chunk_length_buf), "%" G_GINT64_MODIFIER "x\r\n", self->dst_chunk_left); self->dst_chunk_length_ofs = 0; self->dst_chunk_length_end = strlen(self->dst_chunk_length_buf); self->dst_write_state = HTTP_DW_WRITE_CHUNK_LENGTH; } case HTTP_DW_WRITE_CHUNK_LENGTH: { res = z_stream_write(stream, &self->dst_chunk_length_buf[self->dst_chunk_length_ofs], self->dst_chunk_length_end - self->dst_chunk_length_ofs, &bw, &local_error); if (res == G_IO_STATUS_NORMAL) { self->dst_chunk_length_ofs += bw; if (self->dst_chunk_length_ofs == self->dst_chunk_length_end) self->dst_write_state = HTTP_DW_WRITE_CHUNK; } else { break; } /* fallthrough */ } case HTTP_DW_WRITE_CHUNK: { /* NOTE: here lies the assumptions that ZTransfer2 neither * changes the buffer nor count when G_IO_STATUS_AGAIN * is returned */ res = z_stream_write(stream, &buf[count - self->dst_chunk_left], self->dst_chunk_left, &bw, &local_error); if (res == G_IO_STATUS_NORMAL) { self->dst_chunk_length += bw; self->dst_chunk_left -= bw; if (self->dst_chunk_left == 0) { self->dst_write_state = HTTP_DW_WRITE_FOOTER; } else { *bytes_written = 0; res = G_IO_STATUS_AGAIN; break; } } else { break; } } self->dst_footer_ofs = 0; case HTTP_DW_WRITE_FOOTER: { gchar line_buf[] = "\r\n"; g_assert(self->dst_footer_ofs < 2); res = z_stream_write(stream, &line_buf[self->dst_footer_ofs], 2 - self->dst_footer_ofs, &bw, &local_error); if (res == G_IO_STATUS_NORMAL) { self->dst_footer_ofs += bw; if (self->dst_footer_ofs == 2) { self->dst_whole_length += self->dst_chunk_length; *bytes_written = self->dst_chunk_length; self->dst_write_state = HTTP_DW_FORMAT_CHUNK_LENGTH; } } else { break; } } } } else { res = z_stream_write(stream, buf, count, bytes_written, &local_error); } propagate_exit: if (local_error) g_propagate_error(err, local_error); return res; } static GIOStatus http_transfer_dst_shutdown(ZTransfer2 *s, ZStream *stream, GError **err) { HttpTransfer *self = Z_CAST(s, HttpTransfer); GIOStatus res = G_IO_STATUS_NORMAL; GError *local_error = NULL; gsize bw; gboolean delay_transfer; /* delay preamble and closing chunk if we want to write an error page, we can do this if: * * - we are not suppressing data entity (e.g. HEAD) * - we are expecting data (e.g. response) or the request contains data (e.g. POST) */ delay_transfer = (self->dst_write_state == HTTP_DW_INITIAL && (!!(s->status & (ZT2S_FAILED+ZT2S_ABORTED)) || (self->super.stack_decision != ZV_ACCEPT))) && !self->suppress_data && (self->expect_data || self->content_length != HTTP_LENGTH_NONE); if (!delay_transfer) { if (self->dst_write_state == HTTP_DW_INITIAL || self->dst_write_state == HTTP_DW_WRITE_PREAMBLE) { self->dst_write_state = HTTP_DW_WRITE_PREAMBLE; res = http_transfer_dst_write_preamble(self, stream, &local_error); } if (res == G_IO_STATUS_NORMAL) { if (self->content_length != HTTP_LENGTH_NONE && self->dst_chunked) { gchar line_buf[] = "0\r\n\r\n"; res = z_stream_write(stream, line_buf, 5, &bw, &local_error); if (bw != 5) { res = G_IO_STATUS_ERROR; } } } } if (local_error) g_propagate_error(err, local_error); return res; } static gboolean http_transfer_stack_proxy(ZTransfer2 *s, ZStackedProxy **stacked) { HttpTransfer *self = Z_CAST(s, HttpTransfer); ZPolicyObj *proxy_stack_tuple = NULL, *stack_object = NULL; gint side = self->transfer_from; gint stack_type = HTTP_STK_NONE; gboolean called; gboolean success = FALSE; HttpProxy *owner = Z_CAST(s->owner, HttpProxy); HttpHeaders *headers = &owner->headers[self->transfer_from]; HttpHeader *hdr; /* query python for a stacked proxy */ if (self->suppress_data || (self->transfer_type != HTTP_TRANSFER_NORMAL && self->transfer_type != HTTP_TRANSFER_TO_BLOB)) { *stacked = NULL; return TRUE; } z_policy_lock(self->super.owner->thread); proxy_stack_tuple = z_policy_call(self->super.owner->handler, "requestStack", z_policy_var_build("(i)", side), &called, self->super.owner->session_id); if (called && !proxy_stack_tuple) { goto unref_unlock; } if (!z_policy_var_parse(proxy_stack_tuple, "(iO)", &stack_type, &stack_object)) { /*LOG This message indicates that the request_stack or response_stack hash contains an invalid stacking tuple. It should contain a (stack_type, proxy_class) tuple. Check your Zorp configuration. */ z_proxy_log(self->super.owner, HTTP_POLICY, 3, "Invalid stacking tuple returned by policy; side='%d'", side); goto unref_unlock; } if (stack_type < HTTP_STK_NONE || stack_type > HTTP_STK_MIME) { /*LOG This message indicates that the request_stack or response_stack hash contains an invalid stacking type. Check your Zorp configuration. */ z_proxy_log(self->super.owner, HTTP_POLICY, 3, "Invalid stacking type; type='%d'", stack_type); stack_type = HTTP_STK_NONE; goto unref_unlock; } success = TRUE; 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))) { *stacked = NULL; goto unref_unlock; } if (stack_type != HTTP_STK_NONE) success = z_proxy_stack_object(s->owner, stack_object, stacked, NULL); unref_unlock: z_policy_var_unref(proxy_stack_tuple); z_policy_unlock(self->super.owner->thread); return success; } static gboolean http_transfer_setup(ZTransfer2 *s) { HttpTransfer *self = Z_CAST(s, HttpTransfer); HttpProxy *owner = Z_CAST(s->owner, HttpProxy); HttpHeaders *headers = &owner->headers[self->transfer_from]; gboolean chunked; /* original entity is chunked */ z_proxy_enter(owner); if (!self->suppress_data) { if (http_lookup_header(headers, "Transfer-Encoding", &self->transfer_encoding_hdr)) { chunked = self->transfer_encoding_hdr->present && strcasecmp(self->transfer_encoding_hdr->value->str, "chunked") == 0; } else { chunked = FALSE; self->transfer_encoding_hdr = http_add_header(headers, "Transfer-Encoding", 17, "", 0); } if (http_lookup_header(headers, "Content-Length", &self->content_length_hdr)) { gchar *end; if (self->content_length_hdr->present) { self->content_length = strtoll(self->content_length_hdr->value->str, &end, 10); if (self->content_length < 0) { self->content_length = self->expect_data ? HTTP_LENGTH_UNKNOWN : HTTP_LENGTH_NONE; } if ((guint) (end - self->content_length_hdr->value->str) != self->content_length_hdr->value->len) { /* content-length not a number */ /*LOG This message indicates that the Content-Length headers value is not a valid number. It is likely caused by a buggy client or server. */ z_proxy_log(owner, HTTP_VIOLATION, 1, "The header 'Content-Length' was present, but is not a number; content_length='%s'", self->content_length_hdr->value->str); z_proxy_return(owner, FALSE); } } else { self->content_length = self->expect_data || chunked ? HTTP_LENGTH_UNKNOWN : HTTP_LENGTH_NONE; } } else { self->content_length_hdr = http_add_header(headers, "Content-Length", 14, "", 0); self->content_length = self->expect_data || chunked ? HTTP_LENGTH_UNKNOWN : HTTP_LENGTH_NONE; } self->transfer_encoding_hdr->present = FALSE; self->content_length_hdr->present = FALSE; if (self->push_mime_headers) { 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; self->dst_chunked = FALSE; if (self->transfer_type != HTTP_TRANSFER_NORMAL) { if (self->transfer_type == HTTP_TRANSFER_TO_BLOB) { /* we are transferring into the request_data blob, we don't want chunking in the destination */ if (chunked) self->src_chunked = TRUE; } else { /* we are transferring from the request_data blob to the * server, we don't need chunking as we know the exact size of * the data */ self->content_length = owner->request_data->size; g_string_sprintf(self->content_length_hdr->value, "%" G_GINT64_FORMAT, owner->request_data->size); self->content_length_hdr->present = TRUE; } } else if (self->super.stacked) { if (chunked && (self->transfer_to == EP_SERVER || (owner->proto_version[self->transfer_to] > 0x0100))) { self->src_chunked = TRUE; self->dst_chunked = TRUE; } else if (self->transfer_to == EP_CLIENT && self->expect_data) { if (owner->max_body_length && self->content_length != HTTP_LENGTH_UNKNOWN && (guint) self->content_length > owner->max_body_length) { self->content_length = owner->max_body_length; self->force_nonpersistent_mode = TRUE; } if (owner->proto_version[self->transfer_to] > 0x0100 && owner->proto_version[self->transfer_from] >= 0x0100) { /* The original entity is not chunked, we enchunk it to * avoid bufferring and keeping the connection open. For * this we need HTTP/1.1 on the client and at least * HTTP/1.0 on the server to be able to transmit headers. * * Transfer-Encoding is added, Content-Length is removed, * version is bumped to HTTP/1.1 */ self->src_chunked = FALSE; self->dst_chunked = TRUE; owner->proto_version[self->transfer_from] = 0x0101; } else { /* chunking is not supported, the entity's end is indicated by an EOF * neither Content-Length nor Transfer-Encoding is added, persisency * is retained only if a content-length hint is received. */ if (owner->proto_version[self->transfer_from] >= 0x0100) self->persistent_with_cl_hint_only = TRUE; else self->force_nonpersistent_mode = TRUE; self->src_chunked = self->dst_chunked = FALSE; } } else { /* sending to the server, the client sends it unchunked, but we add chunking to avoid buffering */ /* NOTE: some servers do not accept chunked data for methods other * than POST, but those do not usually have entity either */ if (self->content_length > 0 || (self->expect_data && self->content_length == HTTP_LENGTH_UNKNOWN)) { self->src_chunked = FALSE; self->dst_chunked = TRUE; /* NOTE: we only change server protocol version as * otherwise some defaults might change (e.g. connection * mode) */ if (self->transfer_from == EP_SERVER) owner->proto_version[self->transfer_from] = 0x0101; } else if (self->content_length != 0) { self->content_length = HTTP_LENGTH_NONE; } } } else { /* there's no stacked proxy */ if (chunked) { self->src_chunked = TRUE; self->dst_chunked = TRUE; } else if (self->content_length >= 0) { /* entity with specified length */ if (owner->max_body_length && self->content_length != HTTP_LENGTH_UNKNOWN && (guint) self->content_length > owner->max_body_length) { self->content_length = owner->max_body_length; self->force_nonpersistent_mode = TRUE; } } else if (self->content_length == HTTP_LENGTH_UNKNOWN) { /* EOF terminated entity, can only happen for server->client direction */ g_assert(self->transfer_from == EP_SERVER); if (owner->keep_persistent && owner->proto_version[self->transfer_to] > 0x0100 && owner->proto_version[self->transfer_from] >= 0x0100) { /* client supports chunking, server can transfer headers, convert it */ self->src_chunked = FALSE; self->dst_chunked = TRUE; owner->server_connection_mode = HTTP_CONNECTION_CLOSE; owner->proto_version[self->transfer_from] = 0x0101; } else if (owner->connection_mode == HTTP_CONNECTION_KEEPALIVE) { /* client does not support chunking or we are not in keep_persistent mode, * no way to keep it persistent */ g_string_assign(owner->connection_hdr->value, "close"); owner->connection_hdr->present = TRUE; owner->connection_mode = HTTP_CONNECTION_CLOSE; } } } /* NOTE: the headers here are not final, if a content-length hint is received, the * content-length header will be added and transfer-encoding removed */ if (self->dst_chunked) { self->content_length_hdr->present = FALSE; self->transfer_encoding_hdr->present = TRUE; g_string_assign(self->transfer_encoding_hdr->value, "chunked"); } else if (self->content_length >= 0) { self->transfer_encoding_hdr->present = FALSE; g_string_sprintf(self->content_length_hdr->value, "%" G_GINT64_FORMAT, self->content_length); self->content_length_hdr->present = TRUE; } } else { self->content_length = HTTP_LENGTH_NONE; } switch (self->content_length) { case HTTP_LENGTH_UNKNOWN: self->super.our_content_length_hint_set = FALSE; break; case HTTP_LENGTH_NONE: default: self->super.our_content_length_hint_set = TRUE; self->super.our_content_length_hint = self->content_length; if (self->super.our_content_length_hint > 0) self->super.our_content_length_hint += self->stacked_preamble->len; break; } self->format_preamble_func((HttpProxy *) self->super.owner, FALSE, self->preamble); z_proxy_return(owner, TRUE); } static ZTransfer2Result http_transfer_run(ZTransfer2 *s) { HttpTransfer *self = Z_CAST(s, HttpTransfer); GError *local_error = NULL; if ((self->content_length != HTTP_LENGTH_NONE || self->push_mime_headers) && self->content_length != 0) { return Z_SUPER(self, ZTransfer2)->run(&self->super); } if (z_transfer2_src_shutdown(s, z_transfer2_get_stream(s, ZT2E_SOURCE), &local_error) != G_IO_STATUS_NORMAL) { g_error_free(local_error); return ZT2_RESULT_ABORTED; } if (z_transfer2_dst_shutdown(s, z_transfer2_get_stream(s, ZT2E_DEST), &local_error) != G_IO_STATUS_NORMAL) { g_error_free(local_error); return ZT2_RESULT_ABORTED; } return ZT2_RESULT_FINISHED; } static HttpTransfer * http_transfer_new(HttpProxy *owner, gint transfer_type, guint from, ZStream *from_stream, guint to, ZStream *to_stream, gboolean expect_data, gboolean suppress_data, HttpTransferPreambleFunc format_preamble) { HttpTransfer *self; z_proxy_enter(owner); self = Z_CAST(z_transfer2_new(Z_CLASS(HttpTransfer), &owner->super, owner->poll, from_stream, to_stream, owner->buffer_size, owner->timeout, 0), HttpTransfer); self->transfer_from = from; self->transfer_to = to; self->transfer_type = transfer_type; self->format_preamble_func = format_preamble; self->preamble = g_string_sized_new(0); self->stacked_preamble = g_string_sized_new(0); self->force_nonpersistent_mode = FALSE; self->expect_data = expect_data; self->suppress_data = suppress_data; z_proxy_return(owner, self); } static void http_transfer_free_method(ZObject *s) { HttpTransfer *self = Z_CAST(s, HttpTransfer); g_string_free(self->preamble, TRUE); g_string_free(self->stacked_preamble, TRUE); z_transfer2_free_method(s); } ZTransfer2Funcs http_transfer_funcs = { { Z_FUNCS_COUNT(ZTransfer2), http_transfer_free_method, }, .src_read = http_transfer_src_read, .dst_write = http_transfer_dst_write, .src_shutdown = http_transfer_src_shutdown, .dst_shutdown = http_transfer_dst_shutdown, .stack_proxy = http_transfer_stack_proxy, .setup = http_transfer_setup, .run = http_transfer_run, .progress = NULL }; Z_CLASS_DEF(HttpTransfer, ZTransfer2, http_transfer_funcs); gboolean http_data_transfer(HttpProxy *self, gint transfer_type, guint from, ZStream *from_stream, guint to, ZStream *to_stream, gboolean expect_data, gboolean suppress_data, HttpTransferPreambleFunc format_preamble) { HttpTransfer *t; gboolean res = TRUE; ZTransfer2Result tr; /* * tell transfer not to send the preamble when data is expected (e.g. * response & response body). This makes it possible to display an error * page instead */ if (transfer_type != HTTP_TRANSFER_TO_BLOB) { guint one = 1; guint fd = z_stream_get_fd(to_stream); setsockopt(fd, SOL_TCP, TCP_CORK, &one, sizeof(one)); } t = http_transfer_new(self, transfer_type, from, from_stream, to, to_stream, expect_data, suppress_data, format_preamble); if (!t || !z_transfer2_start(&t->super)) { /*LOG This message indicates that the processed request was invalid, and the data transfer failed. */ z_proxy_log(self, HTTP_ERROR, 2, "Invalid request, data transfer failed;"); g_string_assign(self->error_info, "Invalid request, data transfer failed;"); if (t) z_object_unref(&t->super.super); return FALSE; } do { tr = z_transfer2_run(&t->super); } while (tr == ZT2_RESULT_SUSPENDED); if (transfer_type != HTTP_TRANSFER_TO_BLOB) { guint zero = 0; guint fd = z_stream_get_fd(to_stream); setsockopt(fd, SOL_TCP, TCP_CORK, &zero, sizeof(zero)); } if (tr == ZT2_RESULT_FAILED) z_transfer2_rollback(&t->super); res = (tr != ZT2_RESULT_FAILED) && (tr != ZT2_RESULT_ABORTED); if (!res) { /* transfer was not successful */ /*LOG This message reports that the data transfer failed. */ z_proxy_log(self, HTTP_ERROR, 2, "Data transfer failed;"); g_string_assign(self->error_info, "Data transfer failed."); } /* transfer was successful, check if the stacked proxy told us something important */ if (t->super.stack_decision != ZV_ACCEPT) { /*LOG This message indicates that the stacked proxy returned the specified verdict about the content. Check the stacked proxy log for further information. */ z_proxy_log(self, HTTP_ERROR, 2, "Stacked proxy decision; verdict='%d', info='%s'", t->super.stack_decision, t->super.stack_info->str); if (t->super.stack_info->len) g_string_assign(self->error_info, t->super.stack_info->str); else g_string_sprintf(self->error_info, "Stacked proxy did not accept this content (%d).", t->super.stack_decision); } else if (res) { /*LOG This message indicates that the stacked proxy accepted the content. */ z_proxy_log(self, HTTP_DEBUG, 6, "Stacked proxy accepted data;"); } if (t->dst_write_state == HTTP_DW_INITIAL) { /* our write state is HTTP_DW_INITIAL, this means that we never transmitted: * * a request/response line * * headers * * data body * This means that we need to return with some kind of error code to ensure * that something gets back to the client */ if (t->super.stack_decision != ZV_ACCEPT) self->error_code = HTTP_MSG_BAD_CONTENT; else self->error_code = HTTP_MSG_IO_ERROR; res = FALSE; } else if (!res) { /* unable to write error page */ self->error_code = 0; } z_object_unref(&t->super.super); return res; } zorp-3.9.5/modules/http/httpftp.c000066400000000000000000000613761172670260400170300ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "http.h" #include #include /* NOTE: cross site scripting * * we may not add anything unescaped to the HTML output as otherwise cross * site scripting would become possible. This includes the HTML page and * HTTP headers. */ enum { HTTP_FTP_FETCH_DIR = 0, HTTP_FTP_FETCH_FILE = 1 }; /** * http_ftp_fetch_response: * @self: HttpProxy instance * @status: returned FTP status code * @msg: returned FTP message * @msglen: size of the @msg buffer * * This function is called to fetch a response from the FTP server. Line * continuations are supported however only the first line will be returned * in @msg. **/ static gboolean http_ftp_fetch_response(HttpProxy *self, gint *status, gchar *msg, gsize msglen) { gchar *line; gsize length; gboolean continuation = TRUE, first = TRUE; gint mul, value, i; msg[0] = 0; while (continuation) { if (z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &length, NULL) != G_IO_STATUS_NORMAL) return FALSE; if (length < 4) { /*LOG This message indicates that the response given by the FTP server was too short, not even the mandatory status code was included. */ z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid FTP response, line too short; line='%.*s'", (gint)length, line); return FALSE; } value = 0; mul = 100; for (i = 0; i < 3; i++) { if (!isdigit(line[i])) { /*LOG This message indicates that the FTP server gave an invalid response, the status code returned by the server was not numeric. */ z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid FTP response, response code not numeric; line='%.*s'", (gint)length, line); return FALSE; } value = value + mul * (line[i] - '0'); mul = mul / 10; } if (first) { gint copy = MIN(msglen-1, length - 4 + 1); *status = value; memcpy(msg, &line[4], copy); msg[copy] = 0; } else if (*status != value) { /*LOG This message indicates that the FTP server gave an invalid response as the status code changed from the one which was present on the first line. */ z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid FTP response, continuation line contains different status code; ftp_status='%d', line='%.*s'", *status, (gint)length, line); return FALSE; } continuation = line[3] == '-'; } return TRUE; } /** * http_ftp_send_command: * @self: HttpProxy instance * @cmd: FTP command to send * @param: parameter to @cmd * * Send a command to the FTP server. **/ static gboolean http_ftp_send_command(HttpProxy *self, const gchar *cmd, const gchar *param) { gchar request_msg[1024]; gsize bw; if (param) g_snprintf(request_msg, sizeof(request_msg), "%s %s\r\n", cmd, param); else g_snprintf(request_msg, sizeof(request_msg), "%s\r\n", cmd); if (z_stream_write(self->super.endpoints[EP_SERVER], request_msg, strlen(request_msg), &bw, NULL) != G_IO_STATUS_NORMAL) return FALSE; return TRUE; } /** * http_ftp_communicate: * @self: HttpProxy instance * @cmd: FTP command to send * @param: parameter to @cmd * @status: FTP status is returned here * @response_msg: FTP response is returned here * @response_msglen: size of @response_msg * * Send a command and wait for a response from the FTP server. **/ static gboolean http_ftp_communicate(HttpProxy *self, const gchar *cmd, const gchar *param, gint *status, gchar *response_msg, gsize response_msglen) { if (!http_ftp_send_command(self, cmd, param)) return FALSE; if (!http_ftp_fetch_response(self, status, response_msg, response_msglen)) return FALSE; return TRUE; } /** * http_ftp_login: * @self: HttpProxy instance * @user: username to log in with * @passwd: password for @user * * Log in to the FTP server using credentials in @user and @passwd. **/ static gboolean http_ftp_login(HttpProxy *self, const gchar *user, const gchar *passwd) { gchar response_msg[1024]; gint status; if (!http_ftp_communicate(self, "USER", user, &status, response_msg, sizeof(response_msg))) return FALSE; if (status == 230) { /* no password required */ return TRUE; } else if (status != 331) { /* no password required */ g_string_sprintf(self->error_info, "Unhandled status code returned to login request (%d, %s)", status, response_msg); /*LOG This message indicates that the FTP server returned an unknown status code in response to our login request. */ z_proxy_log(self, HTTP_ERROR, 4, "FTP server returned an unhandled status code for the login request; ftp_status='%d'", status); return FALSE; } if (!http_ftp_communicate(self, "PASS", passwd, &status, response_msg, sizeof(response_msg))) return FALSE; if (status == 230) { /* logged in */ return TRUE; } else { g_string_sprintf(self->error_info, "Error logging in (%d, %s)", status, response_msg); /*LOG This message indicates that the FTP refused our authentication attempt. */ z_proxy_log(self, HTTP_ERROR, 4, "Error logging in; user='%s', ftp_status='%d', ftp_response='%s'", user, status, response_msg); return FALSE; } } /** * http_ftp_setcwd: * @self: HttpProxy instance * @cwd: directory to change into * * Set the current working directory on the FTP server to @cwd. **/ static gboolean http_ftp_setcwd(HttpProxy *self, const gchar *cwd) { gchar response_msg[1024]; gint status; if (!http_ftp_communicate(self, "CWD", cwd, &status, response_msg, sizeof(response_msg))) return FALSE; if (status != 250) { return FALSE; } return TRUE; } /** * http_ftp_set_type: * @self: HttpProxy instance * @type: transfer type * * Set the transfer type to ascii (A) or binary (I). **/ static gboolean http_ftp_set_type(HttpProxy *self, const gchar *type) { gchar response_msg[1024]; gint status; if (!http_ftp_communicate(self, "TYPE", type, &status, response_msg, sizeof(response_msg))) return FALSE; if (status != 200) { return FALSE; } return TRUE; } /** * http_ftp_initiate_passive_data: * @self: HttpProxy instance * * This function is called to initiate a passive data connection to the FTP * server. If it returns FALSE an active connection might still be * attempted. (however it is currently unimplemented). * * It currently creates the ZAttach object only does not start the actual * connection, it is done somewhat later in the http_ftp_complete_data() * function. **/ static gboolean http_ftp_initiate_passive_data(HttpProxy *self) { gchar response_msg[1024]; gint status; gchar *start, *end; gint i; gint ftp_pasv_endpoint[6]; gchar ip[16]; ZSockAddr *peer; ZAttachParams params; if (!http_ftp_communicate(self, "PASV", NULL, &status, response_msg, sizeof(response_msg))) return FALSE; if (status != 227) return FALSE; start = strchr(response_msg, '('); if (!start) { /* hmm no '(' in PASV response */ return FALSE; } start++; for (i = 0; i < 6; i++) { ftp_pasv_endpoint[i] = strtol(start, &end, 10); if ((i < 5 && *end != ',') || (i == 5 && *end != ')')) { g_string_sprintf(self->error_info, "Response to PASV is invalid; response='%s'", response_msg); return FALSE; } start = end + 1; } g_snprintf(ip, sizeof(ip), "%d.%d.%d.%d", ftp_pasv_endpoint[0], ftp_pasv_endpoint[1], ftp_pasv_endpoint[2], ftp_pasv_endpoint[3]); peer = z_sockaddr_inet_new(ip, 256 * ftp_pasv_endpoint[4] + ftp_pasv_endpoint[5]); memset(¶ms, 0, sizeof(params)); params.timeout = 30000; self->ftp_data_attach = z_attach_new(&self->super, ZD_PROTO_TCP, NULL, peer, ¶ms, NULL, NULL, NULL); z_sockaddr_unref(peer); /* attach not started yet */ return TRUE; } /** * http_ftp_initiate_passive_data: * @self: HttpProxy instance * * This function is called to initiate an active data connection to the FTP * server. * * This function is only a placeholder as active-mode data transfer is not * yet implemented. **/ static gboolean http_ftp_initiate_active_data(HttpProxy *self G_GNUC_UNUSED) { return FALSE; } /** * http_ftp_cleanup_data: * @self: HttpProxy instance * * This function is called to clean up the FTP data state, e.g. dispose * self->ftp_data_attach. **/ static inline void http_ftp_cleanup_data(HttpProxy *self) { z_attach_free(self->ftp_data_attach); self->ftp_data_attach = NULL; } /** * http_ftp_complete_data: * @self: HttpProxy * @data_stream: the resulting data stream * * This function is called to complete the data connection setup and should * return with the negotiated data channel in @data_stream. It currently * supports passive mode only. **/ static gboolean http_ftp_complete_data(HttpProxy *self, ZStream **data_stream) { ZConnection *conn; gboolean success; /* FIXME: supports PASV only */ success = z_attach_start_block(self->ftp_data_attach, &conn); http_ftp_cleanup_data(self); if (!success || !conn) { *data_stream = NULL; return FALSE; } else { *data_stream = z_stream_ref(conn->stream); z_connection_destroy(conn, FALSE); return TRUE; } } /** * http_ftp_format_response: * @self: HttpProxy instance * @stacked: whether headers towards the stacked proxy need to be formatted * @response: resulting HTTP response * * This function is passed as a function pointer to HttpTransfer whenever it * needs to format the preamble to be sent prior to the first data byte. **/ static gboolean http_ftp_format_response(HttpProxy *self, gboolean stacked, GString *response) { if (self->proto_version[EP_CLIENT] >= 0x0100) { http_flat_headers(&self->headers[EP_SERVER]); g_string_sprintf(response, "Content-Type: application/octet-stream\r\n" "%s\r\n", self->headers[EP_SERVER].flat->str); if (!stacked) { g_string_prepend(response, "HTTP/1.0 200 OK\r\n"); } } else g_string_truncate(response, 0); return TRUE; } #define SKIP_SPACES \ do \ { \ while (left && *src == ' ') \ { \ src++; \ left--; \ } \ } \ while (0) #define HTTP_FTP_MAX_FILES 16384 #define HTTP_FTP_FILE_WIDTH 32 /** * http_ftp_htmlize_listing: * @self: HttpProxy instance * @data_stream: stream to fetch LIST output from * @http_response: GString to store HTML output into * * This function reads the incoming LIST output from the FTP server and * reformats it in HTML so that browsers can simply display it. NOTE: * maximum HTTP_FTP_MAX_FILES of lines are processed as the HTTP response * and body is formatted in memory. * * Returns: TRUE for success, FALSE otherwise **/ static gboolean http_ftp_htmlize_listing(HttpProxy *self, ZStream *data_stream, GString *http_response) { ZStream *list; gchar *line; gsize length; gchar perm[16]; gchar owner[32], group[32], size[16], date[13], *filename; gsize fill; gint file_count = 0, filelen, i; gchar spaces[HTTP_FTP_FILE_WIDTH + 3 + 1]; z_stream_set_timeout(data_stream, self->timeout); memset(spaces, ' ', sizeof(spaces)); spaces[HTTP_FTP_FILE_WIDTH] = 0; for (i = sizeof(spaces) - 1; i > 0; i -= 3) spaces[i] = '.'; /* request_url is canonicalized, thus it might only contain US-ASCII * characters, anything else will be escaped */ g_string_sprintfa(http_response, "\n" "\n" "FTP directory %s\n" "\n" "\n" "

FTP directory %s

\n" "
\n", VERSION, self->request_url->str, self->request_url->str, self->request_url->str);
        
  g_string_sprintfa(http_response, 
        "Parent directory\n");
  list = z_stream_line_new(data_stream, 4096, ZRL_EOL_NL | ZRL_TRUNCATE);
  while (file_count < HTTP_FTP_MAX_FILES && z_stream_line_get(list, &line, &length, NULL) == G_IO_STATUS_NORMAL)
    {
      gchar *src = line;
      gint left = length;
      
      fill = 0;
      while (left && *src && *src != ' ' && fill < sizeof(perm) - 1)
        {
          perm[fill] = *src;
          src++;
          left--;
          fill++;
        }
      perm[fill] = 0;
      
      /* link count */
      SKIP_SPACES;
      while (left && *src && *src != ' ')
        {
          src++;
          left--;
        }
      /* owner */
      SKIP_SPACES;
      fill = 0;
      while (left && *src && *src != ' ' && fill < sizeof(owner) - 1)
        {
          owner[fill] = *src;
          src++;
          left--;
          fill++;
        }
      owner[fill] = 0;

      /* group */
      SKIP_SPACES;
      fill = 0;
      while (left && *src && *src != ' ' && fill < sizeof(group) - 1)
        {
          group[fill] = *src;
          src++;
          left--;
          fill++;
        }
      group[fill] = 0;

      /* size */
      SKIP_SPACES;
      fill = 0;
      while (left && *src && *src != ' ' && fill < sizeof(size) - 1)
        {
          size[fill] = *src;
          src++;
          left--;
          fill++;
        }
      size[fill] = 0;
      
      /* date */
      SKIP_SPACES;
      fill = 0;
      while (left && *src && fill < sizeof(date) - 1)
        {
          date[fill] = *src;
          src++;
          left--;
          fill++;
        }
      date[fill] = 0;
      
      SKIP_SPACES;
      filelen = 0;
      
      filename = src;
      while (left && *src && *src != ' ' && *src != '\n' && *src != '\r')
        {
          src++;
          filelen++;
          left--;
        }
        
      if (filelen > 0)
        {
          if (perm[0] == 'd')
            {
              filename[filelen] = '/';
              filelen++;
            }
          
          /* FIXME: escape HTML output properly */
          g_string_sprintfa(http_response, 
                "%.*s   %.*s  %s  %12s bytes\n", 
                      filelen, filename, 
                      MIN(filelen, HTTP_FTP_FILE_WIDTH), filename, 
                      MAX(HTTP_FTP_FILE_WIDTH - filelen, 0), spaces + filelen % 3,
                      date, size);
        }      
      file_count++;
    }
  if (file_count >= HTTP_FTP_MAX_FILES)
    {
      g_string_sprintfa(http_response, "Too many files, listing truncated (limit is %d)\n", HTTP_FTP_MAX_FILES);
    }
  g_string_sprintfa(http_response, "
"); z_stream_unref(list); return TRUE; } #undef SKIP_SPACES /** * http_handle_ftp_request: * @self: HttpProxy instance * * Handle an incoming non-transparent FTP request if permit_ftp_over_http is * enabled. Connects to the upstream server using the FTP protocol. **/ gboolean http_handle_ftp_request(HttpProxy *self) { gint status; gchar response_msg[1024]; const gchar *user, *passwd, *fetch_fname = NULL; gchar *slash; gint fetch = HTTP_FTP_FETCH_DIR; ZStream *data_stream; gboolean res = TRUE; http_clear_headers(&self->headers[EP_SERVER]); self->connection_hdr = http_add_header(&self->headers[EP_SERVER], "Proxy-Connection", 16, "close", 5); self->connection_hdr->present = TRUE; if (!http_connect_server(self)) { return FALSE; } if (!http_ftp_fetch_response(self, &status, response_msg, sizeof(response_msg))) { return FALSE; } if (status != 220) { /*LOG This message indicates that the FTP server greeted Zorp with a non-220 response code. */ z_proxy_log(self, HTTP_ERROR, 3, "Error in FTP greeting, expecting 220; ftp_status='%d'", status); } if (self->request_url_parts.user->len) { user = self->request_url_parts.user->str; passwd = self->request_url_parts.passwd->str; } else { user = "anonymous"; passwd = "ftp@"; } if (!http_ftp_login(self, user, passwd)) { return FALSE; } /* check whether request_url_parts.file ends with '/' => directory */ /* try to cd into request_url_parts.file, if successful => directory */ /* try to cd into path of request_url_parts.file, if successful => file, if not => error */ /* if file => fetch file, if directory => fetch and format dirlist */ if (self->request_url_parts.file->str[self->request_url_parts.file->len - 1] == '/') { fetch = HTTP_FTP_FETCH_DIR; slash = &self->request_url_parts.file->str[self->request_url_parts.file->len - 1]; if (slash != self->request_url_parts.file->str) *slash = '\0'; if (!http_ftp_setcwd(self, self->request_url_parts.file->str)) { *slash = '/'; g_string_sprintf(self->error_info, "Directory not found on server. (%s)", self->request_url_parts.file->str); /*LOG This message indicates that the directory specified in the URL was not accepted by the FTP server. */ z_proxy_log(self, HTTP_ERROR, 4, "Error changing directory on server; dir='%s'", self->request_url_parts.file->str); return FALSE; } *slash = '/'; } else { if (http_ftp_setcwd(self, self->request_url_parts.file->str)) { const gchar *reason; GString *url = g_string_sized_new(0); g_string_append_c(self->request_url_parts.file, '/'); if (!http_format_url(&self->request_url_parts, url, TRUE, FALSE, TRUE, &reason)) { g_string_assign(url, "Error formatting URL"); } self->error_code = HTTP_MSG_REDIRECT; self->error_status = 301; g_string_assign(self->error_info, url->str); g_string_sprintf(self->error_msg, "Redirect"); /* NOTE: url is properly escaped, as it was canonicalized by * http_format_url above */ g_string_sprintfa(self->error_headers, "Location: %s\r\n", url->str); g_string_free(url, TRUE); return FALSE; } else { slash = strrchr(self->request_url_parts.file->str, '/'); if (!slash) { /* no backslash (or only first backslash present), we already tried to CD into this */ g_string_sprintf(self->error_info, "Directory not found on server. (%s)", self->request_url_parts.file->str); /*LOG This message indicates that the directory specified in the URL was not accepted by the FTP server. */ z_proxy_log(self, HTTP_ERROR, 4, "Error changing directory on server; dir='%s'", self->request_url_parts.file->str); return FALSE; } if (slash != self->request_url_parts.file->str) { *slash = '\0'; if (!http_ftp_setcwd(self, self->request_url_parts.file->str)) { g_string_sprintf(self->error_info, "Directory not found on server. (%s)", self->request_url_parts.file->str); /*LOG This message indicates that the directory specified in the URL was not accepted by the FTP server. */ z_proxy_log(self, HTTP_ERROR, 4, "Error changing directory on server; dir='%s'", self->request_url_parts.file->str); *slash = '/'; return FALSE; } *slash = '/'; } fetch = HTTP_FTP_FETCH_FILE; fetch_fname = slash + 1; } } if (!http_ftp_set_type(self, (fetch == HTTP_FTP_FETCH_FILE) ? "I" : "A")) { z_proxy_log(self, HTTP_ERROR, 4, "Error setting FTP transfer type;"); g_string_assign(self->error_info, "Error setting FTP transfer type."); return FALSE; } /* ok, everything ok, set up data channel */ if (!http_ftp_initiate_passive_data(self) && !http_ftp_initiate_active_data(self)) { return FALSE; } g_string_sprintf(self->error_info, "Error downloading file %s", self->request_url->str); /* fetch file pointed to by fetch_fname */ if (!http_ftp_send_command(self, fetch == HTTP_FTP_FETCH_FILE ? "RETR" : "LIST", fetch == HTTP_FTP_FETCH_FILE ? fetch_fname : NULL)) { /*LOG This message indicates that an I/O error occurred while sending our RETR or LIST request. */ z_proxy_log(self, HTTP_ERROR, 4, "Error sending RETR or LIST command to FTP server;"); http_ftp_cleanup_data(self); return FALSE; } if (!http_ftp_complete_data(self, &data_stream)) { /*LOG This message indicates that an I/O error occurred while trying to establish the data connection with the FTP server. */ z_proxy_log(self, HTTP_ERROR, 4, "Error establishing data connection to FTP server;"); return FALSE; } if (!http_ftp_fetch_response(self, &status, response_msg, sizeof(response_msg)) || (status != 150 && status != 125)) { /* file was successfully downloaded, finished */ z_stream_shutdown(data_stream, SHUT_RDWR, NULL); z_stream_close(data_stream, NULL); z_stream_unref(data_stream); g_string_sprintf(self->error_info, "FTP error: %d %s", status, response_msg); /*LOG This message indicates that the FTP server did not react with a 150 response after the data connection was established. */ z_proxy_log(self, HTTP_ERROR, 4, "Error reading data channel confirmation from FTP server; status='%d', response='%s'", status, response_msg); return FALSE; } if (fetch == HTTP_FTP_FETCH_FILE) { if (!http_data_transfer(self, HTTP_TRANSFER_NORMAL, EP_SERVER, data_stream, EP_CLIENT, self->super.endpoints[EP_CLIENT], TRUE, FALSE, http_ftp_format_response)) { /* file was not successfully downloaded, finished */ res = FALSE; } } else { GString *http_response = g_string_sized_new(128); g_string_sprintf(http_response, "HTTP/1.0 200 OK\r\n" "Content-Type: text/html\r\n" "Proxy-Connection: close\r\n\r\n"); if (!http_ftp_htmlize_listing(self, data_stream, http_response)) { res = FALSE; } else { if (!http_write(self, EP_CLIENT, http_response->str, http_response->len)) res = FALSE; } g_string_free(http_response, TRUE); } /* file was successfully downloaded, finished */ z_stream_shutdown(data_stream, SHUT_RDWR, NULL); z_stream_close(data_stream, NULL); z_stream_unref(data_stream); if (res) { status = 999; if (!http_ftp_fetch_response(self, &status, response_msg, sizeof(response_msg)) || status != 226) { /*LOG This message indicates that the FTP server did not return with an appropriate status code at the end of the data transfer. */ z_proxy_log(self, HTTP_ERROR, 4, "Error reading 226 from FTP server; status='%d', response='%s'", status, response_msg); /* we already sent the page, thus we may not return FALSE here */ } } return res; } zorp-3.9.5/modules/http/httphdr.c000066400000000000000000000574341172670260400170140ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * Based on the code by: Viktor Peter Kovacs * ***************************************************************************/ #include "http.h" #include #include /* these headers are not allowed to be duplicated, the first header is * used, the rest is dropped. All headers that is likely to be used * for an access control decision should be added here */ static gchar *smuggle_headers[] = { "Content-Length", /* makes complete request smuggling possible */ "Transfer-Encoding", /* trick the proxy to use a different transfer-encoding than the server */ "Content-Type", /* different content-types */ "Host", /* different hostname in URL, e.g. http://goodsite.org/index.html instead of http://evilsite.org/index.html */ "Connection", /* different connection mode, might cause the connection to stall */ "Proxy-Connection", /* -"- */ "Authorization", /* different credentials (username/password) */ "Proxy-Authorization" /* -"- */ }; gboolean http_header_equal(gconstpointer k1, gconstpointer k2) { return g_strcasecmp(k1, k2) == 0; } guint http_header_hash(gconstpointer key) { const char *p = key; guint h = toupper(*p); if (h) for (p += 1; *p != '\0'; p++) h = (h << 5) - h + toupper(*p); return h; } static void http_header_free(HttpHeader *h) { g_string_free(h->name, TRUE); g_string_free(h->value, TRUE); g_free(h); } void http_log_headers(HttpProxy *self, gint side, gchar *tag) { HttpHeaders *hdrs = &self->headers[side]; if ((side == EP_CLIENT && z_log_enabled(HTTP_REQUEST, 7)) || (side == EP_SERVER && z_log_enabled(HTTP_RESPONSE, 7))) { GList *l = g_list_last(hdrs->list); while (l) { HttpHeader *hdr = (HttpHeader *) l->data; if (hdr->present) { if (side == EP_CLIENT) /*NOLOG*/ z_proxy_log(self, HTTP_REQUEST, 7, "Request %s header; hdr='%s', value='%s'", tag, hdr->name->str, hdr->value->str); else /*NOLOG*/ z_proxy_log(self, HTTP_RESPONSE, 7, "Response %s header; hdr='%s', value='%s'", tag, hdr->name->str, hdr->value->str); } l = g_list_previous(l); } } } /* duplicated headers are simply put on the list and not inserted into the hash, thus looking up a header by name always results the first added header */ HttpHeader * http_add_header(HttpHeaders *hdrs, gchar *name, gint name_len, gchar *value, gint value_len) { HttpHeader *h; HttpHeader *orig; h = g_new0(HttpHeader, 1); h->name = g_string_sized_new(name_len + 1); g_string_assign_len(h->name, name, name_len); h->value = g_string_sized_new(value_len + 1); g_string_assign_len(h->value, value, value_len); h->present = TRUE; if (!http_lookup_header(hdrs, h->name->str, &orig)) { hdrs->list = g_list_prepend(hdrs->list, h); g_hash_table_insert(hdrs->hash, h->name->str, hdrs->list); } else { guint i; for (i = 0; i < sizeof(smuggle_headers) / sizeof(smuggle_headers[0]); i++) { if (strcmp(smuggle_headers[i], h->name->str) == 0) { http_header_free(h); h = NULL; break; } } if (h) { /* not found in smuggle_headers */ hdrs->list = g_list_prepend(hdrs->list, h); } else { z_log(NULL, HTTP_VIOLATION, 3, "Possible smuggle attack, removing header duplication; header='%.*s', value='%.*s', prev_value='%.*s'", name_len, name, value_len, value, (gint) orig->value->len, orig->value->str); } } return h; } static gboolean http_clear_header(gpointer key G_GNUC_UNUSED, gpointer value G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED) { return TRUE; } void http_clear_headers(HttpHeaders *hdrs) { GList *l; for (l = hdrs->list; l; l = l->next) http_header_free(l->data); g_list_free(hdrs->list); hdrs->list = NULL; g_string_truncate(hdrs->flat, 0); g_hash_table_foreach_remove(hdrs->hash, http_clear_header, NULL); } gboolean http_lookup_header(HttpHeaders *headers, gchar *what, HttpHeader **p) { GList *l; l = g_hash_table_lookup(headers->hash, what); if (l) { *p = l->data; return TRUE; } *p = NULL; return FALSE; } static void http_insert_headers(gchar *key, ZPolicyObj *f, HttpHeaders *hdrs) { guint filter_type; gchar *value; if (!z_policy_tuple_get_verdict(f, &filter_type)) { /* filter has no type field */ return; } switch (filter_type) { case HTTP_HDR_INSERT: case HTTP_HDR_REPLACE: if (!z_policy_var_parse(f, "(is)", &filter_type, &value)) { /* error parsing HTTP_INSERT or HTTP_REPLACE rule */ return; } http_add_header(hdrs, key, strlen(key), value, strlen(value)); break; default: break; } } static gboolean http_check_header_charset(HttpProxy *self, gchar *header, guint flags, const gchar **reason) { gint i; *reason = FALSE; if (flags & HTTP_HDR_FF_ANY) return TRUE; if (flags & HTTP_HDR_FF_URL) { HttpURL url; gboolean success; http_init_url(&url); success = http_parse_url(&url, self->permit_unicode_url, self->permit_invalid_hex_escape, TRUE, header, reason); http_destroy_url(&url); return success; } for (i = 0; header[i]; i++) { gboolean ok; guchar c = header[i]; ok = FALSE; if ((flags & HTTP_HDR_CF_ALPHA) && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) ok = TRUE; else if ((flags & HTTP_HDR_CF_NUMERIC) && (c >= '0' && c <= '9')) ok = TRUE; else if ((flags & HTTP_HDR_CF_SPACE) && (c == ' ')) ok = TRUE; else if ((flags & HTTP_HDR_CF_COMMA) && (c == ',')) ok = TRUE; else if ((flags & HTTP_HDR_CF_DOT) && (c == '.')) ok = TRUE; else if ((flags & HTTP_HDR_CF_BRACKET) && (c == '[' || c == ']' || c == '{' || c == '}' || c == '(' || c == ')')) ok = TRUE; else if ((flags & HTTP_HDR_CF_EQUAL) && (c == '=')) ok = TRUE; else if ((flags & HTTP_HDR_CF_DASH) && (c == '-')) ok = TRUE; else if ((flags & HTTP_HDR_CF_SLASH) && (c == '/')) ok = TRUE; else if ((flags & HTTP_HDR_CF_COLON) && (c == ':')) ok = TRUE; else if ((flags & HTTP_HDR_CF_SEMICOLON) && (c == ';')) ok = TRUE; else if ((flags & HTTP_HDR_CF_AT) && (c == '@')) ok = TRUE; else if ((flags & HTTP_HDR_CF_UNDERLINE) && (c == '_')) ok = TRUE; else if ((flags & HTTP_HDR_CF_AND) && (c == '&')) ok = TRUE; else if ((flags & HTTP_HDR_CF_BACKSLASH) && (c == '\\')) ok = TRUE; else if ((flags & HTTP_HDR_CF_ASTERIX) && (c == '*')) ok = TRUE; else if ((flags & HTTP_HDR_CF_DOLLAR) && (c == '$')) ok = TRUE; else if ((flags & HTTP_HDR_CF_HASHMARK) && (c == '#')) ok = TRUE; else if ((flags & HTTP_HDR_CF_PLUS) && (c == '+')) ok = TRUE; else if ((flags & HTTP_HDR_CF_QUOTE) && (c == '"' || c == '\'')) ok = TRUE; else if ((flags & HTTP_HDR_CF_QUESTIONMARK) && (c == '?')) ok = TRUE; else if ((flags & HTTP_HDR_CF_PERCENT) && (c == '%')) ok = TRUE; else if ((flags & HTTP_HDR_CF_TILDE) && (c == '~')) ok = TRUE; else if ((flags & HTTP_HDR_CF_EXCLAM) && (c == '!')) ok = TRUE; if (!ok) { *reason = "Invalid character found"; return FALSE; } } return TRUE; } gboolean http_filter_headers(HttpProxy *self, guint side, HttpHeaderFilter filter) { HttpHeaders *headers = &self->headers[side]; GHashTable *hash = (side == EP_CLIENT) ? self->request_header_policy : self->response_header_policy; gint action; GList *l; z_proxy_enter(self); l = headers->list; while (l) { HttpHeader *h = (HttpHeader *) l->data; ZPolicyObj *f; if (filter) action = filter(self, h->name, h->value); else action = HTTP_HDR_ACCEPT; g_string_assign_len(self->current_header_name, h->name->str, h->name->len); g_string_assign_len(self->current_header_value, h->value->str, h->value->len); f = g_hash_table_lookup(hash, self->current_header_name->str); if (!f) f = g_hash_table_lookup(hash, "*"); if (f) { guint filter_type; ZPolicyObj *handler, *res; gchar *name, *value; z_policy_lock(self->super.thread); if (!z_policy_tuple_get_verdict(f, &filter_type)) { /* filter has no type field */ z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } z_policy_unlock(self->super.thread); switch (filter_type) { case HTTP_HDR_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(f, "(iO)", &filter_type, &handler)) { /* error parsing HTTP_POLICY_CALL rule */ z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } res = z_policy_call_object(handler, z_policy_var_build("(s#s#)", h->name->str, h->name->len, h->value->str, h->value->len), self->super.session_id); if (!z_policy_var_parse(res, "i", &action)) { /*LOG This message indicates that the call-back for the given header was invalid. Check your request_headers and response_headers hashes. */ z_proxy_log(self, HTTP_POLICY, 1, "Policy call-back for header returned invalid value; header='%s'", self->current_header_name->str); z_policy_var_unref(res); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } z_policy_var_unref(res); z_policy_unlock(self->super.thread); g_string_assign_len(h->name, self->current_header_name->str, self->current_header_name->len); g_string_assign_len(h->value, self->current_header_value->str, self->current_header_value->len); break; case HTTP_HDR_INSERT: /* insert header that already exists */ action = HTTP_HDR_ACCEPT; break; case HTTP_HDR_ACCEPT: break; case HTTP_HDR_REPLACE: case HTTP_HDR_DROP: action = HTTP_HDR_DROP; break; case HTTP_HDR_CHANGE_NAME: z_policy_lock(self->super.thread); if (!z_policy_var_parse(f, "(is)", &filter_type, &name)) { /* invalid CHANGE_NAME rule */ /*LOG This message indicates that the HDR_CHANGE_NAME parameter is invalid, for the given header. Check your request_headers and response_headers hashes. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid HTTP_HDR_CHANGE_NAME rule in header processing; header='%s'", self->current_header_name->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } g_string_assign(h->name, name); z_policy_unlock(self->super.thread); action = HTTP_HDR_ACCEPT; break; case HTTP_HDR_CHANGE_VALUE: z_policy_lock(self->super.thread); if (!z_policy_var_parse(f, "(is)", &filter_type, &value)) { /* invalid CHANGE_VALUE rule */ /*LOG This message indicates that the HDR_CHANGE_VALUE parameter is invalid, for the given header. Check your request_headers and response_headers hashes. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid HTTP_HDR_CHANGE_VALUE rule in header processing; header='%s'", self->current_header_name->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } g_string_assign(h->value, value); z_policy_unlock(self->super.thread); action = HTTP_HDR_ACCEPT; break; case HTTP_HDR_CHANGE_BOTH: z_policy_lock(self->super.thread); if (!z_policy_var_parse(f, "(iss)", &filter_type, &name, &value)) { /* invalid CHANGE_BOTH rule */ /*LOG This message indicates that the HDR_CHANGE_BOTH parameter is invalid, for the given header. Check your request_headers and response_headers hashes. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid HTTP_HDR_CHANGE_BOTH rule in header processing; header='%s'", self->current_header_name->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } g_string_assign(h->name, name); g_string_assign(h->value, value); z_policy_unlock(self->super.thread); action = HTTP_HDR_ACCEPT; break; case HTTP_HDR_ABORT: action = HTTP_HDR_ABORT; break; default: action = HTTP_HDR_ABORT; /*LOG This message indicates that the action is invalid, for the given header. Check your request_headers and response_headers hashes. */ z_proxy_log(self, HTTP_POLICY, 1, "Invalid value in header action tuple; header='%s', filter_type='%d'", self->current_header_name->str, filter_type); break; } } if (action == HTTP_HDR_ACCEPT && self->strict_header_checking) { HttpElementInfo *info; const gchar *reason; if (side == EP_CLIENT) info = http_proto_request_hdr_lookup(h->name->str); else info = http_proto_response_hdr_lookup(h->name->str); if (info) { if (info->max_len >= 0 && h->value->len > (guint) info->max_len) { z_proxy_log(self, HTTP_VIOLATION, 3, "Header failed strict checking, value too long; " "header='%s', value='%s', length='%" G_GSIZE_FORMAT "', max_length='%" G_GSIZE_FORMAT "'", h->name->str, h->value->str, h->value->len, info->max_len); action = self->strict_header_checking_action; goto exit_check; } if (!http_check_header_charset(self, h->value->str, info->flags, &reason)) { z_proxy_log(self, HTTP_VIOLATION, 3, "Header failed strict checking, it contains invalid characters; " "header='%s', value='%s', reason='%s'", h->name->str, h->value->str, reason); action = self->strict_header_checking_action; goto exit_check; } exit_check: ; } else { z_proxy_log(self, HTTP_VIOLATION, 3, "Header failed strict checking, it is unknown; header='%s', value='%s'", h->name->str, h->value->str); action = self->strict_header_checking_action; } } switch (action) { case HTTP_HDR_ACCEPT: break; case HTTP_HDR_DROP: h->present = FALSE; break; default: self->error_code = HTTP_MSG_POLICY_VIOLATION; z_proxy_return(self, FALSE); } l = g_list_next(l); } z_policy_lock(self->super.thread); g_hash_table_foreach(hash, (GHFunc) http_insert_headers, headers); z_policy_unlock(self->super.thread); z_proxy_return(self, TRUE); } gboolean http_fetch_headers(HttpProxy *self, int side) { HttpHeaders *headers = &self->headers[side]; gchar *line; GIOStatus res; gsize line_length; guint count = 0; HttpHeader *last_hdr = NULL; z_proxy_enter(self); http_clear_headers(headers); /* check if we have to fetch request headers */ if (self->proto_version[side] < 0x0100) z_proxy_return(self, TRUE); while (1) { gchar *colon, *value, c; guint name_len; res = z_stream_line_get(self->super.endpoints[side], &line, &line_length, NULL); if (res != G_IO_STATUS_NORMAL) { if (res == G_IO_STATUS_EOF && side == EP_SERVER && self->permit_null_response) break; /*LOG This message indicates that Zorp was unable to fetch headers from the server. Check the permit_null_response attribute. */ z_proxy_log(self, HTTP_ERROR, 3, "Error reading from peer while fetching headers;"); z_proxy_return(self, FALSE); } if (line_length == 0) break; if (*line == ' ' || *line == '\t') { /* continuation line */ /* skip whitespace */ while (line_length && (*line == ' ' || *line == '\t')) { line++; line_length--; } if (last_hdr) { g_string_append_len(last_hdr->value, line, line_length); } else { /* first line is a continuation line? bad */ /*LOG This message indicates that Zorp fetched an invalid header from the server. It is likely caused by a buggy server. */ z_proxy_log(self, HTTP_VIOLATION, 2, "First header starts with white space; line='%.*s", (gint) line_length, line); z_proxy_return(self, FALSE); } goto next_header; } name_len = 0; c = line[name_len]; while (name_len < line_length && !(c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' || c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || c == '{' || c == '}' || c == ' ' || c == '\t')) { name_len++; c = line[name_len]; } for (colon = &line[name_len]; (guint) (colon - line) < line_length && *colon == ' ' && *colon != ':'; colon++) ; if (*colon != ':') { /*LOG This message indicates that the server sent an invalid HTTP header. */ z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid HTTP header; line='%.*s'", (gint) line_length, line); if (self->strict_header_checking) z_proxy_return(self, FALSE); goto next_header; } /* strip trailing white space */ while (line_length > 0 && line[line_length - 1] == ' ') line_length--; for (value = colon + 1; (guint) (value - line) < line_length && *value == ' '; value++) ; last_hdr = http_add_header(headers, line, name_len, value, &line[line_length] - value); next_header: count++; if (count > self->max_header_lines) { /* too many headers */ /*LOG This message indicates that the server tried to send more header lines, than the allowed maximum. Check the max_header_lines attribute. */ z_proxy_log(self, HTTP_POLICY, 2, "Too many header lines; max_header_lines='%d'", self->max_header_lines); z_proxy_return(self, FALSE); } } /* g_string_append(headers, "\r\n"); */ http_log_headers(self, side, "prefilter"); z_proxy_return(self, TRUE); } gboolean http_flat_headers_into(HttpHeaders *hdrs, GString *into) { GList *l = g_list_last(hdrs->list); g_string_truncate(into, 0); while (l) { if (((HttpHeader *) l->data)->present) { g_string_append_len(into, ((HttpHeader *) l->data)->name->str, ((HttpHeader *) l->data)->name->len); g_string_append_len(into, ": ", 2); g_string_append_len(into, ((HttpHeader *) l->data)->value->str, ((HttpHeader *) l->data)->value->len); g_string_append_len(into, "\r\n", 2); } l = g_list_previous(l); } return TRUE; } gboolean http_flat_headers(HttpHeaders *hdrs) { return http_flat_headers_into(hdrs, hdrs->flat); } void http_init_headers(HttpHeaders *hdrs) { hdrs->hash = g_hash_table_new(http_header_hash, http_header_equal); hdrs->flat = g_string_sized_new(256); } void http_destroy_headers(HttpHeaders *hdrs) { http_clear_headers(hdrs); g_hash_table_destroy(hdrs->hash); g_string_free(hdrs->flat, TRUE); } enum { HTTP_COOKIE_NAME, HTTP_COOKIE_VALUE, HTTP_COOKIE_DOTCOMA } HttpCookieState; GHashTable * http_parse_header_cookie(HttpHeaders *hdrs) { GHashTable *cookie_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); HttpHeader *hdr; if (http_lookup_header(hdrs, "Cookie", &hdr)) { gint state = HTTP_COOKIE_NAME; gchar key[256]; guint key_pos = 0; gchar value[4096]; guint value_pos = 0; gint i = 0; gchar *hdr_str = hdr->value->str; while (hdr_str[i]) { switch(state) { case HTTP_COOKIE_NAME: if (hdr_str[i] == '=') { key[key_pos] = 0; state = HTTP_COOKIE_VALUE; } else { key[key_pos++] = hdr_str[i]; } if (key_pos > sizeof(key)) { goto exit_error; } break; case HTTP_COOKIE_VALUE: if (hdr_str[i] == ';') { state = HTTP_COOKIE_DOTCOMA; } else { value[value_pos++] = hdr_str[i]; } if (value_pos > sizeof(value)) { goto exit_error; } break; case HTTP_COOKIE_DOTCOMA: if (hdr_str[i] != ' ' && hdr_str[i] != '\r' && hdr_str[i] != '\n' && hdr_str[i] != '\t') { if (hdr_str[i] == '$' && FALSE) { value[value_pos++] = hdr_str[i]; if (value_pos > sizeof(value)) { goto exit_error; } state = HTTP_COOKIE_VALUE; } else { value[value_pos] = 0; g_hash_table_insert(cookie_hash, g_strdup(key), g_strdup(value)); key_pos = value_pos = 0; key[key_pos++] = hdr_str[i]; state = HTTP_COOKIE_NAME; } } } i++; } if (key_pos && value_pos) { value[value_pos] = 0; g_hash_table_insert(cookie_hash, g_strdup(key), g_strdup(value)); key_pos = value_pos = 0; } goto exit; } exit_error: g_hash_table_destroy(cookie_hash); cookie_hash = NULL; exit: return cookie_hash; } zorp-3.9.5/modules/http/httpmisc.c000066400000000000000000001027331172670260400171630ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * Based on the code by: Viktor Peter Kovacs * ***************************************************************************/ #include "http.h" #include #include #include #define HTTP_URL_HOST_ESCAPE_CHARS "/$&+,;=?@ \"'<>#%{}|\\^~[]`" #define HTTP_URL_USER_ESCAPE_CHARS "/$&+,:;=?@ \"'<>#%{}|\\^~[]`" #define HTTP_URL_PASSWD_ESCAPE_CHARS "/$&+,:;=?@ \"'<>#%{}|\\^~[]`" #define HTTP_URL_FILE_ESCAPE_CHARS "?#% \"<>" #define HTTP_URL_QUERY_ESCAPE_CHARS "/$&+,:;=?@ \"'<>#%{}|\\^~[]`" #define HTTP_URL_FRAGMENT_ESCAPE_CHARS "/$&+,:;=?@ \"'<>#%{}|\\^~[]`" /** * xdigit_value: * @c: possible hexadecimal character * * Return the hexadecimal value of @c or return -1 if not a hexadecimal character. **/ static inline gint xdigit_value(char c) { c = tolower(c); if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') return c - 'a' + 10; return -1; } /** * xdigit_char: * @nibble: half byte to return hexadecimal equivalent for * * Return the hexadecimal character representing @nibble. **/ static inline gchar xdigit_char(gint nibble) { if (nibble >= 0 && nibble <= 9) return nibble + '0'; else if (nibble >= 10 && nibble <= 15) return nibble - 10 + 'A'; return '?'; } /** * http_string_url_decode_hex_byte: * @dst: store decoded value here * @src: read hexadecimal numbers from here * @reason: error reason text if the operation fails * * Convert a hexadecimal encoded byte to the equivalent value. **/ static inline gboolean http_string_url_decode_hex_byte(guchar *dst, const gchar *src, const gchar **reason) { if (isxdigit(*src) && isxdigit(*(src+1))) { *dst = (xdigit_value(*src) << 4) + xdigit_value(*(src+1)); } else { *reason = "Invalid hexadecimal encoding"; return FALSE; } return TRUE; } /** * http_string_assign_url_decode: * @part: store the decoded string here * @permit_invalid_hex_escape: whether to treat invalid url encoding sequences as errors * @src: source string to decode * @len: length of string pointed to by @src * @reason: terror reason text if the operation fails * * Decodes an URL part such as username, password or host name. Assumes * single byte destination encoding, e.g. US-ASCII with the 128-255 range * defined. **/ static gboolean http_string_assign_url_decode(GString *part, gboolean permit_invalid_hex_escape, const gchar *src, gint len, const gchar **reason) { gchar *dst; gint left = len; /* url decoding shrinks the string, using len is a good bet */ g_string_set_size(part, len); dst = part->str; while (left) { guchar c = (guchar) *src; if (*src == '%') { *reason = "Hexadecimal encoding too short"; if (left < 2 || !http_string_url_decode_hex_byte(&c, src+1, reason)) { if (permit_invalid_hex_escape) { /* if the escaping is invalid we emit a literal '%' which * will be escaped in http_format_url */ c = '%'; } else { return FALSE; } } else { src += 2; left -= 2; } } *dst = c; dst++; src++; left--; } *dst = 0; part->len = dst - part->str; /* some space might still be allocated at the end of the string * but we don't care to avoid reallocing and possible data copy */ return TRUE; } /** * http_string_assign_url_decode_unicode: * @part: store the decoded string here * @permit_invalid_hex_escape: whether to treat invalid url encoding sequences as errors * @src: source string to decode * @len: length of string pointed to by @src * @reason: error reason text if the operation fails * * Decodes an URL part such as username, password or host name. Assumes * UTF8 destination encoding, decoding possible %uXXXX sequences as UCS2. **/ static gboolean http_string_assign_url_decode_unicode(GString *part, gboolean permit_invalid_hex_escape, const gchar *str, gint len, const gchar **reason) { const guchar *src = (const guchar *) str; gchar *dst; gint left = len; /* possible maximum utf8 length is 6 times the amount of UCS2 chars, note * that real unicode characters can only be encoded by the %uXXXX encoding * which in turn decreases the number of needed bytes */ g_string_set_size(part, (len + 1) * 6); dst = part->str; while (left) { gunichar c = (gunichar) *src; if (*src == '%') { if (*(src+1) != 'u') { guchar cb; *reason = "Hexadecimal encoding too short"; if (left < 2 || !http_string_url_decode_hex_byte(&cb, src+1, reason)) { if (permit_invalid_hex_escape) { c = '%'; } else return FALSE; } else { c = (gunichar) cb; src += 2; left -= 2; } } else { guchar cbhi, cblo; *reason = "Unicode hexa encoding too short"; if (left < 4 || !http_string_url_decode_hex_byte(&cbhi, src + 2, reason) || !http_string_url_decode_hex_byte(&cblo, src + 4, reason)) { if (permit_invalid_hex_escape) { c = '%'; } else { return FALSE; } } else { c = (cbhi << 8) + cblo; src += 5; left -= 5; } } } dst += g_unichar_to_utf8(c, dst); src++; left--; } *dst = 0; part->len = dst - part->str; /* some space might still be allocated at the end of the string * but we don't care to avoid reallocing and possible data copy */ return TRUE; } /** * http_string_append_url_encode: * @result: store the decoded string here * @unsafe_chars: a string of characters to be URL encoded * @str: source string to decode * @len: length of string pointed to by @src * @reason: error reason text if the operation fails * * Encodes a plain US-ASCII string to be a part of a URL, e.g. encodes * unsafe and non-latin characters using URL hexadecimal encoding. **/ gboolean http_string_append_url_encode(GString *result, const gchar *unsafe_chars, const gchar *str, gint len, const gchar **reason G_GNUC_UNUSED) { const guchar *src; gchar *dst; gsize orig_len = result->len; g_string_set_size(result, orig_len + (len+1) * 3); src = (const guchar *) str; dst = result->str + orig_len; while (*src) { if (*src <= 0x1F || *src > 0x7F || strchr(unsafe_chars, *src)) { *dst = '%'; *(dst+1) = xdigit_char((*src & 0xf0) >> 4); *(dst+2) = xdigit_char((*src & 0x0f)); dst += 2; } else { *dst = *src; } src++; dst++; } *dst = 0; result->len = dst - result->str; return TRUE; } /** * http_string_append_url_encode_unicode: * @result: store the decoded string here * @unsafe_chars: a string of characters to be URL encoded * @str: source string to decode * @len: length of string pointed to by @src * @reason: error reason text if the operation fails * * Encodes an UTF8 string to be a part of a URL, e.g. encodes unsafe and * non-latin characters using URL hexadecimal encoding. **/ gboolean http_string_append_url_encode_unicode(GString *result, const gchar *unsafe_chars, const gchar *str, gint len, const gchar **reason) { const guchar *src; gchar *dst; gsize orig_len = result->len; g_string_set_size(result, orig_len + (len+1) * 6); src = (const guchar *) str; dst = result->str + orig_len; while (*src) { gunichar c = g_utf8_get_char(src); if (c <= 0x1F || (c > 0x7F && c < 0x100) || strchr(unsafe_chars, (gchar) c)) { *dst = '%'; *(dst+1) = xdigit_char((c & 0xf0) >> 4); *(dst+2) = xdigit_char((c & 0x0f)); dst += 2; } else if (c > 0xFF && c < 0x10000) { *dst = '%'; *(dst+1) = 'u'; *(dst+2) = xdigit_char((c & 0xf000) >> 12); *(dst+3) = xdigit_char((c & 0x0f00) >> 8); *(dst+4) = xdigit_char((c & 0x00f0) >> 4); *(dst+5) = xdigit_char((c & 0x000f)); dst += 5; } else if (c > 0xFFFF) { *reason = "Error recoding character, value not fitting into UCS2 found"; return FALSE; } else { *dst = (gchar) c; } src = g_utf8_next_char(src); dst++; } *dst = 0; result->len = dst - result->str; return TRUE; } /** * http_string_assign_url_canonicalize: * @result: store the canonicalized string here * @permit_invalid_hex_escape: whether to treat invalid url encoding sequences as errors * @unsafe_chars: a string of characters to be URL encoded * @str: source string to decode * @len: length of string pointed to by @src * @reason: error reason text if the operation fails * * Decode and encode the parts of an URL making sure that a single possible encoding * form is used. **/ gboolean http_string_assign_url_canonicalize(GString *result, gboolean permit_invalid_hex_escape, const gchar *unsafe_chars, const gchar *str, gint len, const gchar **reason) { gchar *dst; const gchar *src; gint left = len; /* possible maximum utf8 length is 6 times the amount of utf16 chars, note * that real unicode characters can only be encoded by the %uXXXX encoding * which in turn decreases the number of needed bytes */ g_string_set_size(result, (len + 1) * 6); dst = result->str; src = str; while (left) { guchar c = (guchar) *src; gboolean was_encoded = FALSE; if (*src == '%') { *reason = "Hexadecimal encoding too short"; if (left < 2 || !http_string_url_decode_hex_byte(&c, src + 1, reason)) { if (permit_invalid_hex_escape) { c = '%'; } else { return FALSE; } } else { src += 2; left -= 2; was_encoded = TRUE; } } /* ok, character to store is in c, encode it again */ if (c <= 0x1F || (c > 0x7F) || (was_encoded && strchr(unsafe_chars, (gchar) c))) { /* these characters must be encoded regardless of their original form */ *dst = '%'; *(dst+1) = xdigit_char((c & 0xf0) >> 4); *(dst+2) = xdigit_char((c & 0x0f)); dst += 2; } else { *dst = (gchar) c; } dst++; src++; left--; } *dst = 0; result->len = dst - result->str; /* some space might still be allocated at the end of the string * but we don't care to avoid reallocing and possible data copy */ return TRUE; } /** * http_string_assign_url_canonicalize_unicode: * @result: store the canonicalized string here * @permit_invalid_hex_escape: whether to treat invalid url encoding sequences as errors * @unsafe_chars: a string of characters to be URL encoded * @str: source string to decode * @len: length of string pointed to by @src * @reason: error reason text if the operation fails * * Decode and encode the parts of an URL making sure that a single possible * encoding form is used, also permit unicode encoding with %uXXXX * sequences. **/ gboolean http_string_assign_url_canonicalize_unicode(GString *result, gboolean permit_invalid_hex_escape, const gchar *unsafe_chars, const gchar *str, gint len, const gchar **reason) { gchar *dst; const guchar *src; gint left = len; /* possible maximum utf8 length is 6 times the amount of utf16 chars, note * that real unicode characters can only be encoded by the %uXXXX encoding * which in turn decreases the number of needed bytes */ g_string_set_size(result, (len + 1) * 6); dst = result->str; src = (const guchar *) str; while (left) { gunichar c = (gunichar) *src; gboolean was_encoded = FALSE; if (*src == '%') { if (*(src+1) != 'u') { guchar cb; *reason = "Hexadecimal encoding too short"; if (left < 2 || !http_string_url_decode_hex_byte(&cb, src+1, reason)) { if (permit_invalid_hex_escape) { c = '%'; } else { return FALSE; } } else { c = (gunichar) cb; src += 2; left -= 2; } } else { guchar cbhi, cblo; *reason = "Unicode hexa encoding too short"; if (left < 4 || !http_string_url_decode_hex_byte(&cbhi, src + 2, reason) || !http_string_url_decode_hex_byte(&cblo, src + 4, reason)) { if (permit_invalid_hex_escape) { c = '%'; } else { return FALSE; } } else { c = (cbhi << 8) + cblo; src += 5; left -= 5; } } was_encoded = TRUE; } /* ok, character to store is in c, encode it again */ if (c <= 0x1F || (c > 0x7F && c < 0x100)) { /* these characters must be encoded regardless of their original form */ *dst = '%'; *(dst+1) = xdigit_char((c & 0xf0) >> 4); *(dst+2) = xdigit_char((c & 0x0f)); dst += 2; } else if (c < 0x100 && strchr(unsafe_chars, (gchar) c)) { /* unsafe characers are left intact if they were stored unescaped */ if (was_encoded) { *dst = '%'; *(dst+1) = xdigit_char((c & 0xf0) >> 4); *(dst+2) = xdigit_char((c & 0x0f)); dst += 2; } else { *dst = c; } } else if (c > 0xFF && c < 0x10000) { *dst = '%'; *(dst+1) = 'u'; *(dst+2) = xdigit_char((c & 0xf000) >> 12); *(dst+3) = xdigit_char((c & 0x0f00) >> 8); *(dst+4) = xdigit_char((c & 0x00f0) >> 4); *(dst+5) = xdigit_char((c & 0x000f)); dst += 5; } else if (c > 0xFFFF) { *reason = "Error recoding character, value not fitting into UCS2 found"; return FALSE; } else { *dst = (gchar) c; } dst++; src++; left--; } *dst = 0; result->len = dst - result->str; /* some space might still be allocated at the end of the string * but we don't care to avoid reallocing and possible data copy */ return TRUE; } /** * http_parse_url: * @url: store URL parts to this structure * @permit_unicode_url: permit IIS style unicode character encoding * @permit_invalid_hex_escape: permit invalid hexadecimal escaping, treat % in these cases literally * @url_str: URL to parse * @reason: parse error * * Parse the URL specified in @url_str and store the resulting parts in * @url. Scheme, username, password, hostname and filename are stored in * decoded form (UTF8 in permit_unicode_url case), query and fragment are * stored in URL encoded, but canonicalized form. * * Returns: TRUE for success, FALSE otherwise setting @reason to the explanation **/ gboolean http_parse_url(HttpURL *url, gboolean permit_unicode_url, gboolean permit_invalid_hex_escape, gboolean permit_relative_url, gchar *url_str, const gchar **reason) { gchar *p, *end, *part[4], *sep[4], *query_start, *fragment_start, *file_start; gsize file_len, query_len = 0, fragment_len = 0; int i; gboolean inside_brackets = FALSE; z_enter(); g_string_truncate(url->scheme, 0); g_string_truncate(url->user, 0); g_string_truncate(url->passwd, 0); g_string_truncate(url->host, 0); g_string_truncate(url->file, 0); g_string_truncate(url->query, 0); g_string_truncate(url->fragment, 0); url->port = 0; p = url_str; while (*p && *p != ':') p++; if (!*p) { if (!permit_relative_url) { *reason = "URL has no scheme, colon missing"; z_return(FALSE); } else goto relative_url; } if (*(p + 1) != '/' || *(p + 2) != '/') { /* protocol not terminated by '//' */ *reason = "Scheme not followed by '//'"; z_return(FALSE); } g_string_assign_len(url->scheme, url_str, p - url_str); p += 3; for (i = 0; i < 4; i++) { part[i] = p; while (*p && (inside_brackets || *p != ':') && *p != '/' && *p != '@' && *p != '?' && *p != '#') { if (*p == '[') inside_brackets = TRUE; else if (inside_brackets && *p == ']') inside_brackets = FALSE; p++; } sep[i] = p; if (!*p || *p == '/') break; p++; } *reason = "Unrecognized URL construct"; switch (i) { case 0: /* hostname only */ if (!http_string_assign_url_decode(url->host, permit_invalid_hex_escape, part[0], sep[0] - part[0], reason)) z_return(FALSE); break; case 1: /* username && host || hostname && port number */ if (*sep[0] == ':') { if (!http_string_assign_url_decode(url->host, permit_invalid_hex_escape, part[0], sep[0] - part[0], reason)) z_return(FALSE); /* port number */ url->port = strtoul(part[1], &end, 10); if (end != sep[1]) { *reason = "Error parsing port number"; z_return(FALSE); } } else if (*sep[0] == '@') { /* username */ if (!http_string_assign_url_decode(url->user, permit_invalid_hex_escape, part[0], sep[0] - part[0], reason) || !http_string_assign_url_decode(url->host, permit_invalid_hex_escape, part[1], sep[1] - part[1], reason)) z_return(FALSE); } else { z_return(FALSE); } break; case 2: /* username && host && port || username && password && host */ if (*sep[0] == '@' && *sep[1] == ':') { /* username, host, port */ if (!http_string_assign_url_decode(url->user, permit_invalid_hex_escape, part[0], sep[0] - part[0], reason) || !http_string_assign_url_decode(url->host, permit_invalid_hex_escape, part[1], sep[1] - part[1], reason)) z_return(FALSE); url->port = strtoul(part[2], &end, 10); if (end != sep[2]) { *reason = "Error parsing port number"; z_return(FALSE); } } else if (*sep[0] == ':' && *sep[1] == '@') { /* username, password, host */ if (!http_string_assign_url_decode(url->user, permit_invalid_hex_escape, part[0], sep[0] - part[0], reason) || !http_string_assign_url_decode(url->passwd, permit_invalid_hex_escape, part[1], sep[1] - part[1], reason) || !http_string_assign_url_decode(url->host, permit_invalid_hex_escape, part[2], sep[2] - part[2], reason)) z_return(FALSE); } else { z_return(FALSE); } break; case 3: /* username && password && hostname && port */ if (*sep[0] == ':' && *sep[1] == '@' && *sep[2] == ':') { if (!http_string_assign_url_decode(url->user, permit_invalid_hex_escape, part[0], sep[0] - part[0], reason) || !http_string_assign_url_decode(url->passwd, permit_invalid_hex_escape, part[1], sep[1] - part[1], reason) || !http_string_assign_url_decode(url->host, permit_invalid_hex_escape, part[2], sep[2] - part[2], reason)) z_return(FALSE); url->port = strtoul(part[3], &end, 10); if (end != sep[3]) { *reason = "Error parsing port number"; z_return(FALSE); } } else { z_return(FALSE); } break; default: /* not reached */ z_return(FALSE); } if (url->host->str[0] == '[' && url->host->str[url->host->len-1] == ']') { /* IPv6 addresses are surrounded by [], remove them. */ url->need_brackets = TRUE; g_string_erase(url->host, 0, 1); g_string_truncate(url->host, url->host->len-1); } relative_url: file_start = p; if (*file_start != '/') { if (*file_start == '\0') { g_string_assign(url->file, "/"); z_return(TRUE); } *reason = "Invalid path component in URL"; z_return(FALSE); } g_string_assign(url->original_local, file_start); query_start = strchr(p, '?'); fragment_start = strchr(p, '#'); if (query_start && fragment_start) { if (query_start > fragment_start) { *reason = "The fragment part starts earlier than the query"; z_return(FALSE); } file_len = query_start - file_start; query_start++; query_len = fragment_start - query_start; fragment_start++; fragment_len = strlen(fragment_start); } else if (query_start) { file_len = query_start - file_start; query_start++; query_len = strlen(query_start); } else if (fragment_start) { file_len = fragment_start - file_start; fragment_start++; fragment_len = strlen(fragment_start); } else { file_len = strlen(file_start); } if (!(permit_unicode_url ? http_string_assign_url_decode_unicode : http_string_assign_url_decode) (url->file, permit_invalid_hex_escape, file_start, file_len, reason)) z_return(FALSE); /* query and fragment is not url-decoded as it is impossible to get the original back */ if (query_start && !(permit_unicode_url ? http_string_assign_url_canonicalize_unicode : http_string_assign_url_canonicalize) (url->query, permit_invalid_hex_escape, HTTP_URL_QUERY_ESCAPE_CHARS, query_start, query_len, reason)) z_return(FALSE); if (fragment_start && !(permit_unicode_url ? http_string_assign_url_canonicalize_unicode : http_string_assign_url_canonicalize) (url->fragment, permit_invalid_hex_escape, HTTP_URL_FRAGMENT_ESCAPE_CHARS, fragment_start, fragment_len, reason)) z_return(FALSE); z_return(TRUE); } /** * http_format_url: * @url: HttpURL structure * @encode_buf: restructured URL * @format_absolute: whether to forman an absolute or a relative URL * @permit_unicode_url: whether to permit UCS2 encoded characters * @reason: error reason if any * * Reformat the already parsed URL from its internal representation. **/ gboolean http_format_url(HttpURL *url, GString *encode_buf, gboolean format_absolute, gboolean permit_unicode_url, gboolean canonicalized, const gchar **reason) { if (format_absolute) { g_string_assign(encode_buf, url->scheme->str); g_string_append(encode_buf, "://"); if (url->user->len && !http_string_append_url_encode(encode_buf, HTTP_URL_USER_ESCAPE_CHARS, url->user->str, url->user->len, reason)) return FALSE; if (url->passwd->len) { g_string_append_c(encode_buf, ':'); if (!http_string_append_url_encode(encode_buf, HTTP_URL_PASSWD_ESCAPE_CHARS, url->passwd->str, url->passwd->len, reason)) return FALSE; } if (url->user->len || url->passwd->len) g_string_append_c(encode_buf, '@'); if (url->need_brackets) g_string_append_c(encode_buf, '['); if (!http_string_append_url_encode(encode_buf, HTTP_URL_HOST_ESCAPE_CHARS, url->host->str, url->host->len, reason)) return FALSE; if (url->need_brackets) g_string_append_c(encode_buf, ']'); if (url->port) g_string_sprintfa(encode_buf, ":%d", url->port); } if (!canonicalized) { g_string_append(encode_buf, url->original_local->str); } else { if (!(permit_unicode_url ? http_string_append_url_encode_unicode : http_string_append_url_encode) (encode_buf, HTTP_URL_FILE_ESCAPE_CHARS, url->file->str, url->file->len, reason)) { return FALSE; } if (url->query->len) { g_string_append_c(encode_buf, '?'); g_string_append(encode_buf, url->query->str); } if (url->fragment->len) { g_string_append_c(encode_buf, '#'); g_string_append(encode_buf, url->fragment->str); } } return TRUE; } /** * http_init_url: * @url: HttpURL structure * * Initializes the fields in @url. **/ void http_init_url(HttpURL *url) { url->original_local = g_string_sized_new(64); url->scheme = g_string_sized_new(4); url->user = g_string_sized_new(0); url->passwd = g_string_sized_new(0); url->host = g_string_sized_new(32); url->file = g_string_sized_new(64); url->query = g_string_sized_new(0); url->fragment = g_string_sized_new(0); url->need_brackets = FALSE; } /** * http_destroy_url: * @url: HttpURL structure * * Frees all storage associated with @url. **/ void http_destroy_url(HttpURL *url) { g_string_free(url->original_local, TRUE); g_string_free(url->scheme, TRUE); g_string_free(url->user, TRUE); g_string_free(url->passwd, TRUE); g_string_free(url->host, TRUE); g_string_free(url->file, TRUE); g_string_free(url->query, TRUE); g_string_free(url->fragment, TRUE); } #define SKIP_SPACES \ do \ { \ while (left > 0 && *src == ' ') \ { \ src++; \ left--; \ } \ } \ while (0) #define COPY_SPACE \ do \ { \ while (left > 0 && avail > 0 && *src != ' ' && *src) \ { \ *dst++ = *src++; \ left--; \ avail--; \ } \ *dst = 0; \ } \ while (0) /** * http_split_request: * @self: HttpProxy instance * @line: request line * @length: length of @line * * Split an incoming HTTP request to method, url and HTTP version, and store * the resulting items in @self. **/ gboolean http_split_request(HttpProxy *self, gchar *line, gint length) { gchar *src, *dst; gint left, avail; z_proxy_enter(self); g_string_truncate(self->request_method, 0); self->request_flags = -1; self->request_version[0] = 0; g_string_truncate(self->request_url, 0); src = line; left = length; dst = self->request_method->str; avail = self->request_method->allocated_len; COPY_SPACE; self->request_method->len = strlen(self->request_method->str); if (!self->request_method->len || (*src != ' ' && avail == 0)) { /*LOG This message indicates that the request method sent by the client is invalid. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Request method empty, or too long; line='%.*s'", left, src); /* request method empty, or request buffer overflow */ z_proxy_return(self, FALSE); } SKIP_SPACES; avail = self->max_url_length; g_string_truncate(self->request_url, 0); while (left > 0 && avail > 0 && *src != ' ' && *src) { g_string_append_c(self->request_url, *src++); left--; avail--; } if (!self->request_url->str[0] || (*src != ' ' && avail == 0)) { /* url missing, or too long */ /*LOG This message indicates that the URL sent by the client is invalid. */ z_proxy_log(self, HTTP_VIOLATION, 1, "URL missing, or too long; line='%.*s'", left, src); z_proxy_return(self, FALSE); } SKIP_SPACES; dst = self->request_version; avail = sizeof(self->request_version) - 1; COPY_SPACE; if (*src != ' ' && avail == 0) { /* protocol version too long */ /*LOG This message indicates that the protocol version sent by the client is invalid. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Protocol version missing, or too long; line='%.*s'", left, src); z_proxy_return(self, FALSE); } /*LOG This message reports the processed request details. */ z_proxy_log(self, HTTP_REQUEST, 6, "Request details; command='%s', url='%s', version='%s'", self->request_method->str, self->request_url->str, self->request_version); z_proxy_return(self, TRUE); } /** * http_split_request: * @self: HttpProxy instance * @line: request line * @length: length of @line * * Split an incoming HTTP response to HTTP version, status and message * storing the resulting items in @self. **/ gboolean http_split_response(HttpProxy *self, gchar *line, gint line_length) { gchar *src, *dst; gint left, avail; z_proxy_enter(self); self->response_version[0] = 0; self->response[0] = 0; g_string_truncate(self->response_msg, 0); src = line; left = line_length; dst = self->response_version; avail = sizeof(self->response_version) - 1; COPY_SPACE; if (memcmp(self->response_version, "HTTP", 4) != 0) { /* no status line */ /*LOG This message indicates that the server sent an invalid response status line. */ z_proxy_log(self, HTTP_RESPONSE, 6, "Invalid HTTP status line; line='%s'", dst); z_proxy_return(self, FALSE); } if (!self->response_version[0] || (*src != ' ' && avail == 0)) { /* response version empty or too long */ /*LOG This message indicates that the protocol version sent by the server is invalid. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Response version empty or too long; line='%.*s'", line_length, line); z_proxy_return(self, FALSE); } SKIP_SPACES; dst = self->response; avail = sizeof(self->response) - 1; COPY_SPACE; if (!self->response[0] || (*src != ' ' && left && avail == 0)) { /* response code empty or too long */ /*LOG This message indicates that the response code sent by the server is invalid. */ z_proxy_log(self, HTTP_VIOLATION, 1, "Response code empty or too long; line='%.*s'", line_length, line); z_proxy_return(self, FALSE); } self->response_code = atoi(self->response); SKIP_SPACES; avail = 256; while (left > 0 && avail > 0) { g_string_append_c(self->response_msg, *src); src++; left--; avail--; } *dst = 0; /*LOG This message reports the processed response details. */ z_proxy_log(self, HTTP_RESPONSE, 7, "Response details; version='%s', response_code='%s', response_msg='%s'", self->response_version, self->response, self->response_msg->str); z_proxy_return(self, TRUE); } #undef SKIP_SPACES #undef COPY_SPACE /** * http_parse_version: * @self: HttpProxy instance * @side: 0 for request, 1 for response * @version_str: version specification as returned by the peer * * Parse and store an HTTP version supplied in @version_str storing it to * self->proto_version[side]. **/ gboolean http_parse_version(HttpProxy *self, gint side, gchar *version_str) { z_proxy_enter(self); if (strcasecmp(version_str, "HTTP/1.1") == 0) { self->proto_version[side] = 0x0101; } else if (strcasecmp(version_str, "HTTP/1.0") == 0) { self->proto_version[side] = 0x0100; } else if (version_str[0] == 0) { self->proto_version[side] = 0x0009; } else { /* unknown protocol version */ if (side == EP_CLIENT) /*LOG This message indicates that the protocol version sent by the client is unsupported. */ z_proxy_log(self, HTTP_REQUEST, 3, "Unknown protocol version; version='%s'", version_str); else /*LOG This message indicates that the protocol version sent by the server is unsupported. */ z_proxy_log(self, HTTP_RESPONSE, 3, "Unknown protocol version; version='%s'", version_str); self->proto_version[side] = 0x0100; z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } zorp-3.9.5/modules/http/httpproto.c000066400000000000000000000433241172670260400173730ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * Based on the code by: Viktor Peter Kovacs * ***************************************************************************/ #include "http.h" #include static HttpElementInfo request_proto_table[] = { { "HEAD", HTTP_REQ_FLG_HEAD, -1 }, { "OPTIONS", HTTP_REQ_FLG_ASTERIX, -1 }, { "CONNECT", HTTP_REQ_FLG_CONNECT, -1 }, { NULL, 0, 0 }, }; static HttpElementInfo response_proto_table[] = { { "100", HTTP_RESP_FLG_CONTINUE, -1 }, /* unused => hardcoded */ { "101", HTTP_RESP_FLG_SUPPRESS | HTTP_RESP_FLG_STOP, -1 }, /* update to protocol (similar to connect) */ { "204", HTTP_RESP_FLG_SUPPRESS, -1 }, { "205", HTTP_RESP_FLG_DONTEXPECT, -1 }, { "304", HTTP_RESP_FLG_SUPPRESS, -1 }, { "402", HTTP_RESP_FLG_STOP, -1 }, { NULL, 0, 0 }, }; static HttpElementInfo request_hdr_proto_table[] = { { "Allow", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE, 64 }, { "Authorization", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_EQUAL, 256 }, { "Content-Encoding", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH, 64 }, { "Content-Length", HTTP_HDR_CF_NUMERIC, 64 }, { "Content-Type", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_QUOTE, 64 }, { "Date", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "Expires", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON | HTTP_HDR_CF_DASH, 128 }, { "From", HTTP_HDR_CF_AT | HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_DOT | HTTP_HDR_CF_UNDERLINE, 256 }, { "If-Modified-Since", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_SEMICOLON, 128 }, { "Last-Modified", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "Pragma", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH, 64 }, { "Referer", HTTP_HDR_FF_URL, -1 }, { "User-Agent", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_BACKSLASH | HTTP_HDR_CF_DOT | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_DASH | HTTP_HDR_CF_BRACKET | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_COLON, 256 }, // RFC 2068 (HTTP/1.1) { "Accept", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_ASTERIX | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DOT | HTTP_HDR_CF_PLUS, 512 }, { "Accept-Charset", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DOT | HTTP_HDR_CF_ASTERIX, 128 }, { "Accept-Encoding", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_ASTERIX | HTTP_HDR_CF_DOT, 128 }, { "Accept-Language", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DOT, 128 }, { "Accept-Ranges", HTTP_HDR_CF_ALPHA, 64 }, { "Cache-Control", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DASH | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_QUOTE, 128 }, { "Content-Base", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH, 128 }, { "Content-Location", HTTP_HDR_FF_URL, -1 }, { "Content-MD5", HTTP_HDR_FF_ANY, 64 }, { "Content-Range", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH, 64 }, { "ETag", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_COLON | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_HASHMARK | HTTP_HDR_CF_QUOTE, 64 }, { "Host", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON, 512 }, { "If-Match", HTTP_HDR_FF_ANY, 512 }, { "If-None-Match", HTTP_HDR_FF_ANY, 512 }, { "If-Range", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COLON | HTTP_HDR_CF_DOT | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_HASHMARK | HTTP_HDR_CF_DASH, 128 }, { "If-Unmodified-Since", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "Max-Forwards", HTTP_HDR_CF_NUMERIC, 128 }, { "Proxy-Authorization", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_EQUAL, 256 }, { "Range", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_COMMA, 64 }, { "Transfer-Encoding", HTTP_HDR_CF_ALPHA, 64 }, { "Upgrade", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_DOT, 128 }, { "Via", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON | HTTP_HDR_CF_AND | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 512 }, /* RFC 2109 */ { "Cookie", HTTP_HDR_FF_ANY, -1 }, // RFC 2227 { "Meter", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_EQUAL, 128 }, /* RFC 2295 */ { "Accept-Features", HTTP_HDR_FF_ANY, 512 }, { "Negotiate", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_ASTERIX | HTTP_HDR_CF_DASH, 64 }, /* RFC 2518 (WebDAV) */ { "DAV", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE, 64 }, { "Depth", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_ALPHA, 64 }, { "Destination", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON | HTTP_HDR_CF_AND | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 512 }, { "If", HTTP_HDR_FF_ANY, 512 }, { "Lock-Token", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON | HTTP_HDR_CF_AND | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 512 }, { "Overwrite", HTTP_HDR_CF_ALPHA, 8 }, { "Timeout", HTTP_HDR_FF_ANY, 64 }, // RFC 2616 (HTTP/1.1) { "Expect", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_COMMA, 128 }, { "TE", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_DASH | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DOT, 128 }, { "Trailer", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_NUMERIC, 128 }, // RFC 2965 { "Cookie2", HTTP_HDR_FF_ANY, -1 }, // Nokia MMS Extension { "X-NOKIA-MMSC-Message-Id", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_AT, 128 }, { "X-NOKIA-MMSC-Status", HTTP_HDR_CF_NUMERIC, 64 }, { "X-NOKIA-MMSC-Charging", HTTP_HDR_CF_NUMERIC, 8 }, { "X-NOKIA-MMSC-Charged-Party", HTTP_HDR_CF_ALPHA, 64 }, { "X-NOKIA-MMSC-To", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_PLUS | HTTP_HDR_CF_AT | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 128 }, { "X-NOKIA-MMSC-From", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_PLUS | HTTP_HDR_CF_AT | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 128 }, { "X-NOKIA-MMSC-Message-Type", HTTP_HDR_CF_ALPHA, 64 }, { "X-NOKIA-MMSC-Version", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DOT, 16 }, { "Proxy-Connection", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH, 64 }, { "Connection", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE, 64 }, // SOAP 1.1 { "SOAPAction", HTTP_HDR_FF_ANY, -1 }, { NULL, 0, 0 } }; static HttpElementInfo response_hdr_proto_table[] = { // RFC 1945 (HTTP/1.0) { "Allow", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE, 64}, { "Content-Encoding", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH, 64}, { "Content-Length", HTTP_HDR_CF_NUMERIC, 64 }, { "Content-Type", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_QUOTE, 64 }, { "Date", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "Expires", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON | HTTP_HDR_CF_DASH, 128 }, { "Last-Modified", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "Location", HTTP_HDR_FF_URL, -1 }, { "Pragma", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH, 64 }, { "Server", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON | HTTP_HDR_CF_AND | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE | HTTP_HDR_CF_BRACKET | HTTP_HDR_CF_SPACE, 512 }, { "WWW-Authenticate", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DOT | HTTP_HDR_CF_QUOTE, 256 }, // RFC 2065 (HTTP/1.1) { "Age", HTTP_HDR_CF_NUMERIC, 64 }, { "Authorization", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_EQUAL, 256 }, { "Cache-Control", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_DASH | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_QUOTE, 128 }, { "Content-Base", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH, 128 }, { "Content-Location", HTTP_HDR_FF_URL, -1 }, { "Content-MD5", HTTP_HDR_FF_ANY, 64 }, { "Content-Range", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_DASH | HTTP_HDR_CF_SLASH, 64 }, { "ETag", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_SEMICOLON | HTTP_HDR_CF_COLON | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_HASHMARK | HTTP_HDR_CF_QUOTE, 64 }, { "Proxy-Authenticate", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_EQUAL, 256 }, { "Public", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA, 128 }, { "Range", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_COMMA, 64 }, { "Retry-After", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "Transfer-Encoding", HTTP_HDR_CF_ALPHA, 64 }, { "Upgrade", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_DOT, 128 }, { "Vary", HTTP_HDR_FF_ANY, 128 }, { "Via", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON | HTTP_HDR_CF_AND | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_BRACKET, 512 }, { "Warning", HTTP_HDR_FF_ANY, 512 }, // RFC 2069 { "Proxy-Authentication-info", HTTP_HDR_FF_ANY, 512 }, // RFC 2109 { "Set-Cookie", HTTP_HDR_FF_ANY, -1 }, // RFC 2227 { "Meter", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_EQUAL, 128 }, // RFC 2295 { "Alternates", HTTP_HDR_FF_ANY, 512 }, { "TCN", HTTP_HDR_FF_ANY, 128 }, { "Variant-Vary", HTTP_HDR_FF_ANY, 512 }, // RFC 2518 (WebDAV) { "DAV", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE, 64 }, { "Depth", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_ALPHA, 64 }, { "Destination", HTTP_HDR_FF_URL, -1 }, { "If", HTTP_HDR_FF_ANY, 512 }, { "Lock-Token", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_COLON | HTTP_HDR_CF_AND | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 512 }, { "Overwrite", HTTP_HDR_CF_ALPHA, 8 }, { "Status-URI", HTTP_HDR_FF_URL, -1 }, // RFC 2616 (HTTP/1.1) { "Accept-Ranges", HTTP_HDR_CF_ALPHA, 64 }, { "Trailer", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_NUMERIC, 128 }, // RFC 2965 { "Set-Cookie2", HTTP_HDR_FF_ANY, -1 }, // Nokia MMS Extension { "X-NOKIA-MMSC-Message-Id", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_AT, 128 }, { "X-NOKIA-MMSC-Status", HTTP_HDR_CF_NUMERIC, 64 }, { "X-NOKIA-MMSC-Charging", HTTP_HDR_CF_NUMERIC, 8 }, { "X-NOKIA-MMSC-Charged-Party", HTTP_HDR_CF_ALPHA, 64 }, { "X-NOKIA-MMSC-To", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_PLUS | HTTP_HDR_CF_AT | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 128 }, { "X-NOKIA-MMSC-From", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_SLASH | HTTP_HDR_CF_EQUAL | HTTP_HDR_CF_PLUS | HTTP_HDR_CF_AT | HTTP_HDR_CF_DOT | HTTP_HDR_CF_DASH | HTTP_HDR_CF_UNDERLINE, 128 }, { "X-NOKIA-MMSC-Message-Type", HTTP_HDR_CF_ALPHA, 64 }, { "X-NOKIA-MMSC-Version", HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_DOT, 16 }, // Non-rfc(? :o) { "Content-disposition", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH, 64 }, { "Proxy-Connection", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH, 64 }, { "Connection", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DASH | HTTP_HDR_CF_COMMA | HTTP_HDR_CF_SPACE, 64 }, { "X-Cache", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DOT | HTTP_HDR_CF_UNDERLINE | HTTP_HDR_CF_DASH | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE, 128 }, { "X-Cache-Lookup", HTTP_HDR_CF_ALPHA | HTTP_HDR_CF_DOT | HTTP_HDR_CF_UNDERLINE | HTTP_HDR_CF_DASH | HTTP_HDR_CF_NUMERIC | HTTP_HDR_CF_SPACE | HTTP_HDR_CF_COLON, 128 }, { "SOAPAction", HTTP_HDR_FF_ANY, 512 }, { NULL, 0, 0 } }; GHashTable *request_proto_hash; GHashTable *response_proto_hash; GHashTable *request_hdr_proto_hash; GHashTable *response_hdr_proto_hash; gboolean has_url_filter_license; static GHashTable * http_proto_fill_hash(HttpElementInfo *table, gboolean casesens) { GHashTable *hash = NULL; gint x; if (casesens) hash = g_hash_table_new(g_str_hash, g_str_equal); else hash = g_hash_table_new((GHashFunc) http_filter_hash_bucket,(GCompareFunc) http_filter_hash_compare); for (x = 0; table[x].name; x++) g_hash_table_insert(hash, table[x].name, &table[x]); return hash; } void http_proto_init(void) { request_proto_hash = http_proto_fill_hash(request_proto_table, TRUE); response_proto_hash = http_proto_fill_hash(response_proto_table, TRUE); request_hdr_proto_hash = http_proto_fill_hash(request_hdr_proto_table, FALSE); response_hdr_proto_hash = http_proto_fill_hash(response_hdr_proto_table, FALSE); } zorp-3.9.5/modules/http/messages/000077500000000000000000000000001172670260400167655ustar00rootroot00000000000000zorp-3.9.5/modules/http/messages/Makefile.am000066400000000000000000000012741172670260400210250ustar00rootroot00000000000000 ERROR_FILES = clientsyntax.html connecterror.html internal.html \ invalidurl.html ioerror.html policysyntax.html policyviolation.html \ serversyntax.html auth.html servertimeout.html clienttimeout.html badcontent.html ftperror.html \ redirect.html LANGUAGES = en hu de EXTRA_DIST = $(addprefix en/,$(ERROR_FILES)) $(addprefix hu/,$(ERROR_FILES)) $(addprefix de/,$(ERROR_FILES)) pkgdatadir = @datadir@/zorp/http install-data-hook: for lang in $(LANGUAGES); do \ test -d $(DESTDIR)$(pkgdatadir)/$$lang || mkdir -p $(DESTDIR)$(pkgdatadir)/$$lang; \ $(INSTALL) -c -m 644 $(addprefix $(srcdir)/$${lang}/,$(ERROR_FILES)) $(DESTDIR)$(pkgdatadir)/$${lang}; \ done #pkgdata_DATA = $(EXTRA_DIST) zorp-3.9.5/modules/http/messages/de/000077500000000000000000000000001172670260400173555ustar00rootroot00000000000000zorp-3.9.5/modules/http/messages/de/auth.html000066400000000000000000000023561172670260400212120ustar00rootroot00000000000000 Authentifizierung erforderlich.
@INFO@

Authentifizierung erforderlich.

 
zorp-3.9.5/modules/http/messages/de/badcontent.html000066400000000000000000000030231172670260400223620ustar00rootroot00000000000000 Invalid content
@INFO@

Ihre Anfrage ist gemäß lokaler Firewall-Einstellungen nicht zulässig.

Mögliche Ursachen:

  • Der Inhalt den Sie versuchen zu laden ist nicht gestattet

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten
 
zorp-3.9.5/modules/http/messages/de/clientsyntax.html000066400000000000000000000032121172670260400227660ustar00rootroot00000000000000 Invalid request
@INFO@

Ihr Browser sendete eine Anfrage, die nicht verarbeitet werden konnte.

Mögliche Ursachen:

  • Ihr Browser verletzt das HTTP/1.1 Protokoll.
  • Es gibt ein Interoperabilitätsproblem zwischen Ihrem Browser und dem Proxy.

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/clienttimeout.html000066400000000000000000000031341172670260400231310ustar00rootroot00000000000000 Zeitüberschreitung
@INFO@

Zeitüberschreitung.

Mögliche Ursachen:

  • Ihr Browser verletzt das HTTP/1.1 Protokoll.
  • Es gibt ein Interoperabilitätsproblem zwischen Ihrem Browser und dem Proxy.

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/connecterror.html000066400000000000000000000035371172670260400227560ustar00rootroot00000000000000 Es ist ein Problem aufgetreten das Zielsystem zu erreichen
@INFO@

Es ist ein Problem aufgetreten das Zielsystem zu erreichen

Mögliche Ursachen:

  • Das Zielsystem ist nicht angeschlossen oder bedient Anfragen nicht
  • Das Netzwerk des Zielsystems ist nicht erreichbar
  • Firewall-Einstellungen haben Ihre Anfrage nicht zugelassen

Mögliche Lösungen:

  • Prüfen Sie ob Sie die URL korrekt eingegeben haben
  • Stellen Sie sicher, dass die angefragte URL zulässig ist
  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/ftperror.html000066400000000000000000000032331172670260400221070ustar00rootroot00000000000000 Bei der Bearbeitung Ihrer FTP-Anfrage ist ein Fehler aufgetreten
@INFO@

Bei der Bearbeitung Ihrer FTP-Anfrage ist ein Fehler aufgetreten.

Mögliche Ursachen:

  • Es ist ein Netzwerkproblem aufgetreten
  • Der FTP-Server ist mit diesem Proxy nicht interoperabel

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/internal.html000066400000000000000000000023751172670260400220660ustar00rootroot00000000000000 Es ist ein interner Fehler aufgetreten
@INFO@

Es ist ein interner Fehler aufgetreten.

 
zorp-3.9.5/modules/http/messages/de/invalidurl.html000066400000000000000000000034051172670260400224160ustar00rootroot00000000000000 Die URL, die sie laden wollten ist ungültig
@INFO@

Die URL, die sie laden wollten ist ungültig.

Mögliche Ursachen:

  • Der Link den Sie folgen wollten ist falsch.
  • Die URL wurde trotz Übereinstimmung mit den RFC wegen unzulässigen Zeichen verbotenuest contains characters denied by the policy.

Mögliche Lösungen:

  • Nehmen Sie Kontakt mit dem Ersteller der Seite auf.
  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/ioerror.html000066400000000000000000000032441172670260400217270ustar00rootroot00000000000000 Es ist ein I/O-Fehler aufgetreten
@INFO@

Es ist ein I/O-Fehler aufgetreten.

Mögliche Ursachen:

  • Das Netzwerk funktioniert nicht.
  • Bei einer Ein- oder Ausgabeoperation ist eine Zeitüberschreitung aufgetreten.
  • Andere Ein- oder Ausgabefehler sind aufgetreten

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/policysyntax.html000066400000000000000000000031521172670260400230120ustar00rootroot00000000000000 Die lokale Konfiguration ist fehlerhaft oder nicht schlüssig
@INFO@

Die lokale Konfiguration ist fehlerhaft oder nicht schlüssig.

Mögliche Ursachen:

  • Der Administrator hat einen Konfigurationsfehler gemacht.

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/policyviolation.html000066400000000000000000000031131172670260400234650ustar00rootroot00000000000000 Es ist ein Problem aufgetreten das angefragte Zielsystem zu erreichen
@INFO@

Es ist ein Problem aufgetreten das angefragte Zielsystem zu erreichen

Mögliche Ursachen:

  • Sie versuchen eine verbotene Seite zu laden

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/redirect.html000066400000000000000000000001451172670260400220440ustar00rootroot00000000000000 Umgeleitet

Umgeleitet zu @INFO@.

zorp-3.9.5/modules/http/messages/de/serversyntax.html000066400000000000000000000032741172670260400230260ustar00rootroot00000000000000 Unbekannte Antwort. Der Server sendete eine ungültige Antwort
@INFO@

Unbekannte Antwort. Der Server sendete eine ungültige Antwort.

Mögliche Ursachen:

  • Der Server verletzt das HTTP/1.1 Protokoll.
  • Es sind Interoperabilitätsprobleme zwischen dem Server und diesem Gateway aufgetreten.

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/de/servertimeout.html000066400000000000000000000033021172670260400231560ustar00rootroot00000000000000 Zeitüberschreitung der Anfrage an den Server.
@INFO@

Zeitüberschreitung der Anfrage an den Server.

Mögliche Ursachen:

  • Der Server ist nicht erreichbar.
  • Der Server verletzt das HTTP/1.1 Protokoll.
  • Es sind Interoperabilitätsprobleme zwischen dem Server und diesem Gateway aufgetreten.

Mögliche Lösungen:

  • Setzen Sie sich mit Ihrem System-Administrator in Verbindung.
  • Kontaktieren Sie das Zorp Support-Team um Unterstützung zu erhalten.
 
zorp-3.9.5/modules/http/messages/en/000077500000000000000000000000001172670260400173675ustar00rootroot00000000000000zorp-3.9.5/modules/http/messages/en/auth.html000066400000000000000000000023311172670260400212150ustar00rootroot00000000000000 Authentication required
@INFO@

Authentication required.

 
zorp-3.9.5/modules/http/messages/en/badcontent.html000066400000000000000000000027201172670260400223770ustar00rootroot00000000000000 Invalid content
@INFO@

Your request is forbidden by local firewall policy.

Possible reasons:

  • The content you are trying to download is prohibited.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/clientsyntax.html000066400000000000000000000031251172670260400230030ustar00rootroot00000000000000 Invalid request
@INFO@

Your browser sent a request that the gateway did not understand.

Possible reasons:

  • Your browser violates the HTTP/1.1 protocol.
  • There is an interoperability problem between your browser and this proxy.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/clienttimeout.html000066400000000000000000000030511172670260400231410ustar00rootroot00000000000000 Request timed out
@INFO@

Request timed out.

Possible reasons:

  • Your browser violates the HTTP/1.1 protocol.
  • There is an interoperability problem between your browser and this proxy.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/connecterror.html000066400000000000000000000033441172670260400227640ustar00rootroot00000000000000 Connection error
@INFO@

There was a problem connecting the host you specified.

Possible reasons:

  • The target system is down and not servicing requests.
  • The network the target system resides on is unreachable.
  • The firewall policy denied your connection request.

Possible solutions:

  • Check that you typed the URL correctly.
  • Verify that you have access to the specified URL.
  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/ftperror.html000066400000000000000000000031621172670260400221220ustar00rootroot00000000000000 Error while retrieving FTP data
@INFO@

An error occurred while processing your FTP request.

Possible reasons:

  • There was a network related problem while processing your request.
  • There was an interoperability problem between the FTP server and this proxy.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/internal.html000066400000000000000000000023071172670260400220730ustar00rootroot00000000000000 Internal error
@INFO@

Internal error.

 
zorp-3.9.5/modules/http/messages/en/invalidurl.html000066400000000000000000000033501172670260400224270ustar00rootroot00000000000000 Invalid URL
@INFO@

The URL you are trying to fetch is invalid.

Possible reasons:

  • The link you followed is bogus.
  • The browser you are using is bogus.
  • The administrator enabled strict URL checking, and although the URL is RFC compliant, the request contains characters denied by the policy.

Possible solutions:

  • Contact the author of the webpage.
  • Contact the developer of your browser.
  • Contact your system administrator.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/ioerror.html000066400000000000000000000030331172670260400217350ustar00rootroot00000000000000 I/O error
@INFO@

Zorp encountered an I/O error while servicing your request.

Possible reasons:

  • The network is down.
  • An I/O operation timed out.
  • Some other I/O problem occured.

Possible solutions:

  • Contact your system administrator.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/policysyntax.html000066400000000000000000000030361172670260400230250ustar00rootroot00000000000000 A policy problem occurred
@INFO@

Local configuration error. The policy is syntactically incorrect or inconsistent.

Possible reasons:

  • The administrator has made a configuration error.

Possible solutions:

  • Contact your administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/policyviolation.html000066400000000000000000000027341172670260400235070ustar00rootroot00000000000000 Policy violation
@INFO@

There was a problem connecting the host you specified.

Possible reasons:

  • You are trying to download a prohibited URL.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/redirect.html000066400000000000000000000001411172670260400220520ustar00rootroot00000000000000 Redirect

Redirect to @INFO@.

zorp-3.9.5/modules/http/messages/en/serversyntax.html000066400000000000000000000031231172670260400230310ustar00rootroot00000000000000 Invalid response
@INFO@

The server you were trying to contact sent an invalid response.

Possible reasons:

  • The server violates the HTTP/1.1 protocol.
  • There is an interoperability problem between the server and this gateway.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/en/servertimeout.html000066400000000000000000000031461172670260400231760ustar00rootroot00000000000000 Server connection timed out
@INFO@

Server connection request timed out.

Possible reasons:

  • The server became unreachable.
  • The server violates the HTTP/1.1 protocol.
  • There is an interoperability problem between the server and this proxy.

Possible solutions:

  • Contact your system administrator for assistance.
  • Contact your Zorp support team for assistance.
 
zorp-3.9.5/modules/http/messages/hu/000077500000000000000000000000001172670260400174015ustar00rootroot00000000000000zorp-3.9.5/modules/http/messages/hu/auth.html000066400000000000000000000024621172670260400212340ustar00rootroot00000000000000 Authentikációs hiba
@INFO@

Authentikációs hiba!

 
zorp-3.9.5/modules/http/messages/hu/badcontent.html000066400000000000000000000031301172670260400224050ustar00rootroot00000000000000 Illegális tartalom
@INFO@

A tartalom letöltését a tűzfal szabályrendszere nem engedélyezi.

A lehetséges problémák:

  • A letölteni kívánt tartalom nincs engedélyezve.

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
 
zorp-3.9.5/modules/http/messages/hu/clientsyntax.html000066400000000000000000000034151172670260400230170ustar00rootroot00000000000000 Kérelem feldolgozási hiba
@INFO@

A böngésző által küldött kérést a rendszer nem tudja feldolgozni.

A lehetséges problémák:

  • A használt böngésző sérti a HTTP/1.1-es protokollt.
  • Együttműködési probléma lépett fel a böngésző és a kérést kezelő proxy között.

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/clienttimeout.html000066400000000000000000000033601172670260400231560ustar00rootroot00000000000000 A kérés nem érkezett meg időben
@INFO@

A kérés nem érkezett meg időben

A lehetséges problémák:

  • A használt böngésző sérti a HTTP/1.1-es protokollt.
  • Együttműködési probléma lépett fel a böngésző és a kérést kezelő proxy között.

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/connecterror.html000066400000000000000000000035271172670260400230010ustar00rootroot00000000000000 Kapcsolódási hiba
@INFO@

A weboldal nem elérhető.

A lehetséges problémák:

  • A webkiszolgáló szerver nem működik
  • A szerver hálózata nem elérhető
  • A tűzfal szabályrendszere nem engedélyzi ezt a letöltést

A lehetséges megoldások:

  • Ellenőrizze, hogy az adott cím helyesen van megadva
  • Ellenőrizze, hogy az adott cím letöltéséhez van-e jogosultsága
  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/ftperror.html000066400000000000000000000033731172670260400221400ustar00rootroot00000000000000 FTP letöltési hiba
@INFO@

Hiba történt az FTP kapcsolaton való letöltés közben.

A lehetséges problémák:

  • Hálózati hiba történt a kérés feldolgozása közben
  • Együttműködési probléma lépett fel az FTP szerver és a kérést kezelő proxy között.

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/internal.html000066400000000000000000000024661172670260400221130ustar00rootroot00000000000000 Belső hiba lépett fel
@INFO@

Belső hiba lépett fel.

 
zorp-3.9.5/modules/http/messages/hu/invalidurl.html000066400000000000000000000035251172670260400224450ustar00rootroot00000000000000 Invalid URL
@INFO@

A lekért webcím nem megfelelő.

A lehetséges problémák:

  • A hivatkozott webcím hibás
  • A használt böngésző hibás
  • Az adminisztrátor által beállított szigorú URL ellenőrzésnek nem felel meg a lekért webcím

A lehetséges megoldások:

  • Lépjen kapcsolatba a weboldal készítőjével
  • Lépjen kapcsolatba a bböngésző készítőjével
  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/ioerror.html000066400000000000000000000033301172670260400217470ustar00rootroot00000000000000 I/O hiba
@INFO@

A kérés kiszolgálása közben a rendszer I/O hibát érzékelt.

A lehetséges problémák:

  • A hálózat nem elérhető
  • Az I/O művelet közben időtúllépés fordult elő
  • Valamilyen egyéb I/O hiba történt

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/policysyntax.html000066400000000000000000000032231172670260400230350ustar00rootroot00000000000000 Hibás tűzfalkonfiguráció
@INFO@

A tűzfal konfigurációja hibás.

A lehetséges problémák:

  • A tűzfal adminisztrátora hibát vétett a rendszer konfigurálásakor

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/policyviolation.html000066400000000000000000000032201172670260400235100ustar00rootroot00000000000000 Az oldal letöltése nem engedélyezett
@INFO@

A megadott webcím letöltése sikertelen.

A lehetséges problémák:

  • A megadott webcím letöltése nem engedélyezett

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/redirect.html000066400000000000000000000002641172670260400220720ustar00rootroot00000000000000 Átirányítás

Átirányítva ide: @INFO@.

zorp-3.9.5/modules/http/messages/hu/serversyntax.html000066400000000000000000000034411172670260400230460ustar00rootroot00000000000000 Érvénytelen válasz
@INFO@

A megadott szerverhez való kapcsolódás sikertelen, mert az érvénytelen választ küldött.

A lehetséges problémák:

  • Az oldalt kiszolgáló szerver sérti a HTTP/1.1-es protokollt.
  • Együttműködési probléma lépett fel a szerver és a kérést kezelő proxy között.

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/messages/hu/servertimeout.html000066400000000000000000000034571172670260400232150ustar00rootroot00000000000000 Sikertelen kapcsolódás
@INFO@

A szerverhez való kapcsolódás időtűllépés miatt sikertelen.

A lehetséges problémák:

  • A szerver jelenleg nem elérhető
  • Az oldalt kiszolgáló szerver sérti a HTTP/1.1-es protokollt.
  • Együttműködési probléma lépett fel a szerver és a kérést kezelő proxy között.

A lehetséges megoldások:

  • Kérjen segítséget a helyi rendszer adminisztrátorától.
  • Lépjen kapcsolatba a Zorp támogatást nyújtó csapattal
 
zorp-3.9.5/modules/http/tests/000077500000000000000000000000001172670260400163205ustar00rootroot00000000000000zorp-3.9.5/modules/http/tests/Makefile.am000066400000000000000000000004231172670260400203530ustar00rootroot00000000000000LIBS=@MODULETESTS_LIBS@ check_PROGRAMS = http_parse_url http_canon_url http_parse_url_SOURCES = http_parse_url.c http_parse_url_LDADD = ../httpmisc.lo http_canon_url_SOURCES = http_canon_url.c http_canon_url_LDADD = ../httpmisc.lo TESTS = http_parse_url http_canon_url zorp-3.9.5/modules/http/tests/http_canon_url.c000066400000000000000000000145111172670260400215050ustar00rootroot00000000000000#include "../http.h" #define BOOL_STR(x) ((x) ? "TRUE" : "FALSE") gboolean test_case(gint id, gchar *url_str, gboolean unicode, gboolean invalid_escape, gboolean format_absolute, gboolean canonicalize, gchar *expected_url_str) { HttpURL url; gchar *fail_reason = NULL; const gchar *error_reason = NULL; gboolean ok = TRUE, valid; GString *formatted_url = g_string_sized_new(0); http_init_url(&url); valid = http_parse_url(&url, unicode, invalid_escape, FALSE, url_str, &error_reason); if (ok && !valid) { fail_reason = g_strdup_printf("Error parsing URL: %s", !valid ? error_reason : "No error"); ok = FALSE; } if (ok && !http_format_url(&url, formatted_url, format_absolute, unicode, canonicalize, &error_reason)) { fail_reason = g_strdup_printf("Error reformatting URL: %s", error_reason); ok = FALSE; } if (ok && strcmp(formatted_url->str, expected_url_str) != 0) { fail_reason = g_strdup_printf("Canonicalized URL not matching: %s <> %s", formatted_url->str, expected_url_str); ok = FALSE; } g_string_free(formatted_url, TRUE); if (ok) { printf("test success, id=%d, url=%s\n", id, url_str); return TRUE; } else { printf("test failure, id=%d, url=%s, reason=%s\n", id, url_str, fail_reason); g_free(fail_reason); return FALSE; } } struct { gchar *url_str; gboolean invalid_escape; gboolean unicode; gboolean format_absolute; gboolean canonicalize; gchar *expected_url_str; } test_table[] = { { "http://user:pass@test.host:8080/file", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host:8080/file" }, { "http://user:pass@test.host:8080/file?", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host:8080/file?%E9" }, { "http://user:pass@test.host:8080/file?", FALSE, TRUE, TRUE, TRUE, "http://user:pass@test.host:8080/file?%E9" }, { "http://user:pass@test.host:8080/file", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host:8080/file%E9" }, { "http://user:pass@test.host:8080/file", FALSE, TRUE, TRUE, TRUE, "http://user:pass@test.host:8080/file%E9" }, { "http://user:pass@test.host:8080/file", FALSE, FALSE, FALSE, TRUE, "/file" }, { "http://user:pass@test.host/file", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host/file" }, { "http://user:pass@test.host", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host/" }, { "http://user:pass@test.host/file?query#fragment", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host/file?query#fragment" }, { "http://user:pass@test.host/file#fragment", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host/file#fragment" }, { "http://user:pass@test.host/file?query", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host/file?query" }, { "http://user@test.host:8080/file", FALSE, FALSE, TRUE, TRUE, "http://user@test.host:8080/file" }, { "http://user:pass@test.host/file", FALSE, FALSE, TRUE, TRUE, "http://user:pass@test.host/file" }, { "http://user@test.host/file", FALSE, FALSE, TRUE, TRUE, "http://user@test.host/file" }, { "http://test.host:8080/file", FALSE, FALSE, TRUE, TRUE, "http://test.host:8080/file" }, { "http://test.host/file", FALSE, FALSE, TRUE, TRUE, "http://test.host/file" }, { "http://test.host/default.idaNNNN%u9090%u6858%ucbd3", FALSE, TRUE, TRUE, TRUE, "http://test.host/default.idaNNNN%u9090%u6858%uCBD3" }, { "http://test.host/ad/N2558.travelport.telnet/B36496;sz=468x60;ord=%5B%25GMTTIME%25%5D?", FALSE, FALSE, TRUE, TRUE, "http://test.host/ad/N2558.travelport.telnet/B36496;sz=468x60;ord=[%25GMTTIME%25]" }, { "http://test.host/ad/N2558.travelport.telnet/B36496?sz=468x60;ord=%5B%25GMTTIME%25%5D", FALSE, FALSE, TRUE, TRUE, "http://test.host/ad/N2558.travelport.telnet/B36496?sz=468x60;ord=%5B%25GMTTIME%25%5D" }, { "http://user:pass@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26", FALSE, TRUE, TRUE, TRUE, "http://user:pass@test.host/fi/../le?%3Fa&%26" }, { "http://use%72:p%61ss%40@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26#%40", FALSE, TRUE, TRUE, TRUE, "http://user:pass%40@test.host/fi/../le?%3Fa&%26#%40" }, /* Not implemented yet. { "http://use%72:p%61ss%40@test.host/fi%%le", FALSE, FALSE, TRUE, TRUE, "http://user:pass%40@test.host/fi%%le" }, */ /* invalid escaping, invalid_escape disabled */ { "http://use%72:p%61ss%40@test.host/fi%2g%2e%2e%2fle?%u003f%61&%26#%40", TRUE, TRUE, TRUE, TRUE, "http://user:pass%40@test.host/fi%252g../le?%3Fa&%26#%40" }, /* no canonicalization, URL must remain the same, except the username/password part */ { "http://use%72:p%61ss%40@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26#%40", FALSE, TRUE, TRUE, FALSE, "http://user:pass%40@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26#%40" }, { "http://[::1]:80/file", FALSE, FALSE, TRUE, TRUE, "http://[::1]:80/file" }, { NULL, 0,0,0,0, NULL } }; int main(int argc, char *argv[]) { gint i, testcase_id = -1; gint fail_count = 0, success_count = 0; if (argc == 2) testcase_id = atoi(argv[1]); if (testcase_id == -1) { for (i = 0; test_table[i].url_str; i++) { if (test_case(i, test_table[i].url_str, test_table[i].unicode, test_table[i].invalid_escape, test_table[i].format_absolute, test_table[i].canonicalize, test_table[i].expected_url_str)) { success_count++; } else { fail_count++; } } printf("Report: %d success, %d failed\n", success_count, fail_count); } else { i = testcase_id; test_case(i, test_table[i].url_str, test_table[i].unicode, test_table[i].invalid_escape, test_table[i].format_absolute, test_table[i].canonicalize, test_table[i].expected_url_str); } return !(fail_count == 0); } zorp-3.9.5/modules/http/tests/http_parse_url.c000066400000000000000000000212451172670260400215230ustar00rootroot00000000000000#include "../http.h" #define BOOL_STR(x) ((x) ? "TRUE" : "FALSE") #define TEST_STR(field) \ do \ { \ if (ok && \ ((field && memcmp(field, url. field->str, url. field->len) != 0) || \ (!field && url. field->len))) \ { \ fail_reason = g_strdup_printf("Returned and expected value for " #field " mismatch: %s <> %s", url. field->str, field); \ ok = FALSE; \ } \ } \ while (0) gboolean test_case(gint id, gchar *url_str, gboolean unicode, gboolean invalid_escape, gboolean expected_valid, gchar *scheme, gchar *user, gchar *passwd, gchar *host, guint port, gchar *file, gchar *query, gchar *fragment) { HttpURL url; gchar *fail_reason = NULL; const gchar *error_reason = NULL; gboolean ok = TRUE, valid; http_init_url(&url); valid = http_parse_url(&url, unicode, invalid_escape, FALSE, url_str, &error_reason); if (ok && valid != expected_valid) { fail_reason = g_strdup_printf("Parse result different than expected: %s", !valid ? error_reason : "No error"); ok = FALSE; } if (valid) { TEST_STR(scheme); TEST_STR(user); TEST_STR(passwd); TEST_STR(host); if (ok && port && port != url.port) { fail_reason = g_strdup("Return and expected value for port mismatch"); ok = FALSE; } TEST_STR(file); TEST_STR(query); TEST_STR(fragment); } if (ok) { printf("test success, id=%d, url=%s\n", id, url_str); return TRUE; } else { printf("test failure, id=%d, url=%s, reason=%s\n", id, url_str, fail_reason); g_free(fail_reason); return FALSE; } } struct { gchar *url_str; gboolean invalid_escape; gboolean unicode; gboolean valid; gchar *scheme; gchar *user; gchar *passwd; gchar *host; guint port; gchar *file; gchar *query; gchar *fragment; } test_table[] = #define NA NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL { { "http://user:pass@test.host:8080/file", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 8080, "/file", NULL, NULL }, { "http://user:pass@test.host/file", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/file", NULL, NULL }, { "http://user:pass@test.host", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/", NULL, NULL }, { "http://user:pass@test.host?", FALSE, FALSE, FALSE, NA }, { "http://user:pass@test.host#", FALSE, FALSE, FALSE, NA }, { "http://user:pass@test.host/file?query#fragment", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/file", "query", "fragment" }, { "http://user:pass@test.host/file#fragment?start", FALSE, FALSE, FALSE, NA }, { "http://user:pass@test.host/file#fragment", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/file", NULL, "fragment" }, { "http://user:pass@test.host/file?query", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/file", "query", NULL }, { "http://user@test.host:8080/file", FALSE, FALSE, TRUE, "http", "user", NULL, "test.host", 8080, "/file", NULL, NULL }, { "http://user:pass@test.host/file", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/file", NULL, NULL }, { "http://user@test.host/file", FALSE, FALSE, TRUE, "http", "user", NULL, "test.host", 0, "/file", NULL, NULL }, { "http://test.host:8080/file", FALSE, FALSE, TRUE, "http", NULL, NULL, "test.host", 8080, "/file", NULL, NULL }, { "http://test.host/file", FALSE, FALSE, TRUE, "http", NULL, NULL, "test.host", 0, "/file", NULL, NULL }, { "http://user:pass:test.host:54/file", FALSE, FALSE, FALSE, NA }, { "http://www.korridor.hu/default.ida?NNNN%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0000%u0090=a", FALSE, FALSE, FALSE, NA }, { "http://test.host/default.idaNNNN%u9090%u6858%ucbd3", FALSE, TRUE, TRUE, "http", NULL, NULL, "test.host", 0, "/default.ida\x4e\x4e\x4e\x4e\xe9\x82\x90\xe6\xa1\x98\xec\xaf\x93", NULL, NULL }, { "http://test.host/ad/N2558.travelport.telnet/B36496;sz=468x60;ord=%5B%25GMTTIME%25%5D?", FALSE, FALSE, TRUE, "http", NULL, NULL, "test.host", 0, "/ad/N2558.travelport.telnet/B36496;sz=468x60;ord=[%GMTTIME%]", NULL, NULL }, { "http://user:pass@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26", FALSE, TRUE, TRUE, "http", "user", "pass", "test.host", 0, "/fi/../le", "%3Fa&%26", NULL }, { "http://use%72:p%61ss%40@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26#%40", FALSE, TRUE, TRUE, "http", "user", "pass@", "test.host", 0, "/fi/../le", "%3Fa&%26", "%40" }, /* invalid escaping, invalid_escape disabled */ { "http://use%72:p%61ss%40@test.host/fi%2g%2e%2e%2fle?%u003f%61&%26#%40", FALSE, TRUE, FALSE, NA }, { "http://use%72:p%61ss%40@test.host/fi%2f%2e%2e%2fle?%u003g%61&%26#%40", FALSE, TRUE, FALSE, NA }, { "http://use%72:p%61ss%40@test.host/fi%2g%2e%2e%2fle?%u003f%61&%26#%40", FALSE, FALSE, FALSE, NA }, { "http://use%72:p%61ss%40@test.host/fi%2g%2e%2e%2fle?%u003f%61&%26#%40", FALSE, FALSE, FALSE, NA }, { "http://use%72:p%61ss%40@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26#%4", FALSE, FALSE, FALSE, NA }, { "http://use%72:p%61ss%40@test.host/fi%2f%2e%2e%2fle?%u003f%61&%26#%u434", FALSE, TRUE, FALSE, NA }, { "http//userpass@test.host/file", FALSE, FALSE, FALSE, NA }, { "http:userpass@test.host/file", FALSE, FALSE, FALSE, NA }, { "http://user:pass@test.host/file?\x1b", FALSE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/file", "%1B", NULL }, /* invalid escaping, invalid_escape, enabled */ { "http://user:pass@test.host/f%2gile", TRUE, FALSE, TRUE, "http", "user", "pass", "test.host", 0, "/f%2gile", NULL, NULL }, { "http://user:pass@test.host/f%u123gile", TRUE, TRUE, TRUE, "http", "user", "pass", "test.host", 0, "/f%u123gile", NULL, NULL }, /* IPv4 and IPv6 addresses */ { "http://127.0.0.1/file", FALSE, FALSE, TRUE, "http", NULL, NULL, "127.0.0.1", 0, "/file", NULL, NULL }, { "http://[1234::1]/file", FALSE, FALSE, TRUE, "http", NULL, NULL, "1234::1", 0, "/file", NULL, NULL }, { NULL, 0, 0, 0, NA } }; int main(int argc, char *argv[]) { gint i, testcase_id = -1; gint fail_count = 0, success_count = 0; if (argc == 2) testcase_id = atoi(argv[1]); if (testcase_id == -1) { for (i = 0; test_table[i].url_str; i++) { if (test_case(i, test_table[i].url_str, test_table[i].unicode, test_table[i].invalid_escape, test_table[i].valid, test_table[i].scheme, test_table[i].user, test_table[i].passwd, test_table[i].host, test_table[i].port, test_table[i].file, test_table[i].query, test_table[i].fragment)) { success_count++; } else { fail_count++; } } printf("Report: %d success, %d failed\n", success_count, fail_count); } else { i = testcase_id; test_case(i, test_table[i].url_str, test_table[i].unicode, test_table[i].invalid_escape, test_table[i].valid, test_table[i].scheme, test_table[i].user, test_table[i].passwd, test_table[i].host, test_table[i].port, test_table[i].file, test_table[i].query, test_table[i].fragment); } return !(fail_count == 0); } zorp-3.9.5/modules/plug/000077500000000000000000000000001172670260400151465ustar00rootroot00000000000000zorp-3.9.5/modules/plug/Makefile.am000066400000000000000000000003511172670260400172010ustar00rootroot00000000000000pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Plug.py pkglib_LTLIBRARIES = libplug.la libplug_la_SOURCES = plug.c EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/plug/Plug.py000066400000000000000000000355201172670260400164340ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : Bazsi ## Auditor : kisza ## Last audited version: 1.10 ## Notes: ## ############################################################################ """ Proxy for transferring data without protocol inspection. This module defines an interface to the Plug proxy. Plug is a simple TCP or UDP circuit, which means that transmission takes place without protocol verification.
Proxy behavior This class implements a general plug proxy, and is capable of optionally disabling data transfer in either direction. Plug proxy reads connection on the client side, then creates another connection at the server side. Arriving responses are sent back to the client. However, it is not a protocol proxy, therefore PlugProxy does not implement any protocol analysis. It offers protection to clients and servers from lower level (e.g.: IP) attacks. It is mainly used to allow traffic pass the firewall for which there is no protocol proxy available. By default plug copies all data in both directions. To change this behavior, set the copy_to_client or copy_to_server attribute to FALSE. Plug supports the use of secondary sessions as described in secondary sessions. Copying of out-of-band data is not supported.
Related standards Plug proxy is not a protocol specific proxy module, therefore it is not specified in standards.
Log level defined in Plug module PLUG_DEBUG "plug.debug"
""" from Zorp import * from Proxy import Proxy PLUG_DEBUG = "plug.debug" class AbstractPlugProxy(Proxy): """ Class encapsulating the abstract Plug proxy. An abstract proxy class for transferring data. copy_to_server TRUE Allow data transfer in the client->server direction. copy_to_client TRUE Allow data transfer in the server->client direction. bandwidth_to_client n/a Read-only variable containing the bandwidth currently used in server->client direction. bandwidth_to_server n/a Read-only variable containing the bandwidth currently used in client->server direction. packet_stats_interval_time 0 The time in milliseconds between two successive packetStats() events. It can be useful when the Quality of Service for the connection is influenced dynamically. Set to 0 to turn packetStats() off. packet_stats_interval_packet 0 The number of passing packages between two successive packetStats() events. It can be useful when the Quality of Service for the connection is influenced dynamically. Set to 0 to turn packetStats() off. stack_proxy Proxy class to stack into the connection. All data is passed to the specified proxy. timeout 60000 I/O timeout in milliseconds. shutdown_soft FALSE If enabled, the two sides of a connection are closed separately. (E.g.: if the server closes the connection the client side connection is held until it is verified that no further data arrives, for example from a stacked proxy.) It is automatically enabled when proxies are stacked into the connection. buffer_size 1500 Size of the buffer used for copying data. secondary_sessions 10 Maximum number of allowed secondary sessions within a single proxy instance. See for details. secondary_mask 0xf Specifies which connections can be handled by the same proxy instance. See for details. """ name = "plug" def __init__(self, session): """ Constructor initializing a PlugProxy instance. This constructor creates and sets up a PlugProxy instance. session SESSION session this instance belongs to """ self.stack_proxy = None Proxy.__init__(self, session) def requestStack(self): """ Function returning the stacked proxy class. This callback is called by the underlying C proxy to query if something is to be stacked into it. It should return the proxy class to be used. Returns the class of the proxy to stack. """ return self.stack_proxy def packetStats(self, client_bytes, client_pkts, server_bytes, server_pkts): """ Function called when the packet_stats_interval is elapsed. This function is called whenever the time interval set in packet_stats_interval elapses, or a given number of packets were transmitted. This event receives packet statistics as parameters. This function can be used in managing the Quality of Service of the connections; e.g.: to terminate connections with excessive bandwidth requirements (for instance to limit the impact of a covert channel opened when using plug instead of a protocol specific proxy). client_bytes Number of bytes transmitted to the client. client_pkts Number of packets transmitted to the client. server_bytes Number of bytes transmitted to the server. server_pkts Number of packets transmitted to the server. """ raise NotImplementedError class PlugProxy(AbstractPlugProxy): """ Class encapsulating the default Plug proxy. A default PlugProxy based on AbstractPlugProxy. """ pass zorp-3.9.5/modules/plug/plug.c000066400000000000000000000235531172670260400162710ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Balazs Scheidler * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PLUG_DEFAULT_BUFSIZE 1500 #define PLUG_DEBUG "plug.debug" #define PLUG_ERROR "plug.error" #define PLUG_POLICY "plug.policy" #define PLUG_SESSION "plug.session" typedef struct _PlugProxy { ZProxy super; ZPoll *poll; ZPlugSessionData session_data; ZPlugSession *session; } PlugProxy; extern ZClass PlugProxy__class; static gboolean plug_packet_stat_event(ZPlugSession *session, guint64 client_bytes, guint64 client_pkts, guint64 server_bytes, guint64 server_pkts, gpointer user_data); static void plug_finish(ZPlugSession *session, gpointer user_data); static void plug_timeout(ZPlugSession *session, gpointer user_data); void plug_config_set_defaults(PlugProxy *self) { z_proxy_enter(self); self->session_data.copy_to_server = TRUE; self->session_data.copy_to_client = TRUE; self->session_data.timeout = 600000; self->session_data.buffer_size = PLUG_DEFAULT_BUFSIZE; self->session_data.packet_stats = plug_packet_stat_event; self->session_data.finish = plug_finish; self->session_data.timeout_cb = plug_timeout; if (self->super.parent_proxy) self->session_data.shutdown_soft = TRUE; z_proxy_leave(self); } void plug_register_vars(PlugProxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "timeout", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.timeout); z_proxy_var_new(&self->super, "copy_to_client", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.copy_to_client); z_proxy_var_new(&self->super, "copy_to_server", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.copy_to_server); z_proxy_var_new(&self->super, "shutdown_soft", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.shutdown_soft); z_proxy_var_new(&self->super, "packet_stats_interval_packet", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.packet_stats_interval_packet); z_proxy_var_new(&self->super, "packet_stats_interval_time", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.packet_stats_interval_time); z_proxy_var_new(&self->super, "buffer_size", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->session_data.buffer_size); /* Zorp 1.4 compatibility */ z_proxy_var_new(&self->super, "packet_stats_interval", Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG, "packet_stats_interval_packet"); z_proxy_return(self); } static gboolean plug_packet_stat_event(ZPlugSession *session G_GNUC_UNUSED, guint64 client_bytes, guint64 client_pkts, guint64 server_bytes, guint64 server_pkts, gpointer user_data) { PlugProxy *self = (PlugProxy *) user_data; ZPolicyObj *res; gboolean called; guint resc; z_policy_lock(self->super.thread); res = z_policy_call(self->super.handler, "packetStats", z_policy_var_build("iiii", (guint32) client_bytes, (guint32) client_pkts, (guint32) server_bytes, (guint32) server_pkts), &called, self->super.session_id); if (called) { resc = ZV_REJECT; if (res) { if (!z_policy_var_parse(res, "i", &resc)) { /*LOG This message is logged when the policy layer returned a non-integer value in its packetStats() function. packetStats() is expected to return ZV_REJECT or ZV_ACCEPT. */ z_proxy_log(self, PLUG_POLICY, 1, "Invalid return value of packetStats(), integer required;"); } else if (resc != ZV_ACCEPT) { /*LOG This message indicates that the verdict returned by the packetStats() function requests to terminate the session. */ z_proxy_log(self, PLUG_POLICY, 1, "packetStats() requested to abort session; verdict='%d'", resc); } } } else { resc = ZV_ACCEPT; } z_policy_var_unref(res); z_policy_unlock(self->super.thread); return resc == ZV_ACCEPT; } static void plug_finish(ZPlugSession *session G_GNUC_UNUSED, gpointer user_data) { PlugProxy *self = (PlugProxy *) user_data; z_proxy_nonblocking_stop(&self->super); } static void plug_timeout(ZPlugSession *session G_GNUC_UNUSED, gpointer user_data) { PlugProxy *self = (PlugProxy *) user_data; z_proxy_log (self, PLUG_SESSION, 3, "Connection timed out; timeout='%d'", self->session_data.timeout); } static gboolean plug_request_stack_event(PlugProxy *self, ZStackedProxy **stacked) { ZPolicyObj *res; gboolean called; gboolean rc = TRUE; z_proxy_enter(self); z_policy_lock(self->super.thread); *stacked = NULL; res = z_policy_call(self->super.handler, "requestStack", NULL, &called, self->super.session_id); if (res) { if (res != z_policy_none) { /* we have to enable soft shutdown if there is a stacked proxy * -- otherwise we might exit before the child proxy has been * given a chance to handle that one of the endpoints has been * closed */ self->session_data.shutdown_soft = TRUE; rc = z_proxy_stack_object(&self->super, res, stacked, NULL); } } else if (called) { rc = FALSE; } z_policy_var_unref(res); z_policy_unlock(self->super.thread); z_proxy_return(self, rc); } static gboolean plug_nonblocking_init(ZProxy *s, ZPoll *poll) { PlugProxy *self = Z_CAST(s, PlugProxy); ZStackedProxy *stacked; z_proxy_enter(self); if (!z_proxy_connect_server(&self->super, NULL, 0)) { z_proxy_leave(self); return FALSE; } if (!plug_request_stack_event(self, &stacked)) { z_proxy_leave(self); return FALSE; } self->session = z_plug_session_new(&self->session_data, self->super.endpoints[EP_CLIENT], self->super.endpoints[EP_SERVER], stacked, &self->super); if (!self->session) { z_proxy_leave(self); return FALSE; } z_plug_session_register_vars(self->session, self->super.dict); if (!z_plug_session_start(self->session, poll)) { z_proxy_leave(self); return FALSE; } z_proxy_leave(self); return TRUE; } static void plug_nonblocking_deinit(ZProxy *s) { PlugProxy *self = Z_CAST(s, PlugProxy); if (self->session) z_plug_session_cancel(self->session); } static gboolean plug_config(ZProxy *s) { PlugProxy *self = (PlugProxy *) s; z_proxy_enter(self); plug_config_set_defaults(self); plug_register_vars(self); if (Z_SUPER(s, ZProxy)->config(s)) z_proxy_return(self, TRUE); z_proxy_return(self, FALSE); } ZProxy * plug_proxy_new(ZProxyParams *params) { PlugProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(PlugProxy), params), PlugProxy); self->super.flags |= ZPF_NONBLOCKING; z_leave(); return &self->super; } static void plug_proxy_free(ZObject *s) { PlugProxy *self = Z_CAST(s, PlugProxy); z_proxy_enter(self); z_plug_session_destroy(self->session); if (self->poll) { z_poll_unref(self->poll); self->poll = NULL; } z_proxy_free_method(s); z_return(); } ZProxyFuncs plug_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), plug_proxy_free, }, .config = plug_config, .startup = NULL, .nonblocking_init = plug_nonblocking_init, .nonblocking_deinit = plug_nonblocking_deinit, .shutdown = NULL, .destroy = NULL, }; Z_CLASS_DEF(PlugProxy, ZProxy, plug_proxy_funcs); gint zorp_module_init(void) { z_registry_add("plug", ZR_PROXY, plug_proxy_new); return TRUE; } zorp-3.9.5/modules/pop3/000077500000000000000000000000001172670260400150605ustar00rootroot00000000000000zorp-3.9.5/modules/pop3/Makefile.am000066400000000000000000000005541172670260400171200ustar00rootroot00000000000000SUBDIRS = . messages pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Pop3.py pkglib_LTLIBRARIES = libpop3.la libpop3_la_SOURCES = pop3.c pop3misc.c pop3cmd.c pop3policy.c pop3data.c pop3auth.c pop3cmd.h pop3.h pop3policy.h pop3misc.h pop3data.h EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/pop3/Pop3.py000066400000000000000000000736551172670260400162730ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : SaSa ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Proxy for the Post Office Protocol version 3. The Pop3 module defines the classes constituting the proxy for the POP3 protocol.
The POP3 protocol Post Office Protocol version 3 (POP3) is usually used by mail user agents (MUAs) to download messages from a remote mailbox. POP3 supports a single mailbox only, it does not support advanced multi-mailbox operations offered by alternatives such as IMAP. The POP3 protocol uses a single TCP connection to give access to a single mailbox. It uses a simple command/response based approach, the client issues a command and a server can respond either positively or negatively.
Protocol elements The basic protocol is the following: the client issues a request (also called command in POP3 terminology) and the server responds with the result. Both commands and responses are line based, each command is sent as a complete line, a response is either a single line or - in case of mail transfer commands - multiple lines. Commands begin with a case-insensitive keyword possibly followed by one or more arguments (such as RETR or DELE). Responses begin with a status indicator ("+OK" or "-ERR") and a possible explanation of the status code (e.g.: "-ERR Permission denied."). Responses to certain commands (usually mail transfer commands) also contain a data attachment, such as the mail body. See the for further details.
POP3 states The protocol begins with the server displaying a greeting message, usually containing information about the server. After the greeting message the client takes control and the protocol enters the AUTHORIZATION state where the user has to pass credentials proving his/her identity. After successful authentication the protocol enters TRANSACTION state where mail access commands can be issued. When the client has finished processing, it issues a QUIT command and the connection is closed.
Bulk transfers Responses to certain commands (such as LIST or RETR) contain a long data stream. This is transferred as a series of lines, terminated by a "CRLF '.' CRLF" sequence, just like in SMTP. POP3 protocol sample +OK POP3 server ready USER account +OK User name is ok PASS password +OK Authentication successful LIST +OK Listing follows 1 5758 2 232323 3 3434 . RETR 1 +OK Mail body follows From: sender@sender.com To: account@receiver.com Subject: sample mail This is a sample mail message. Lines beginning with ..are escaped, another '.' character is perpended which is removed when the mail is stored by the client. . DELE 1 +OK Mail deleted QUIT +OK Good bye
Proxy behavior Pop3Proxy is a module built for parsing messages of the POP3 protocol. It reads and parses COMMANDs on the client side, and sends them to the server if the local security policy permits. Arriving RESPONSEs are parsed as well, and sent to the client if the local security policy permits. It is possible to manipulate both the requests and the responses.
Default policy for commands By default, the proxy accepts all commands recommended in RFC 1939. Additionally, the following optional commands are also accepted: USER, PASS, AUTH. The proxy understands all the commands specified in RFC 1939 and the AUTH command. These additional commands can be enabled manually.
Configuring policies for POP3 commands Changing the default behavior of commands can be done using the hash named request. The hash is indexed by the command name (e.g.: USER or AUTH). See for details. Example for allowing only APOP authentication in POP3 This sample proxy class rejects the USER authentication requests, but allows APOP requests. class APop3(Pop3Proxy): def config(self): Pop3Proxy.config(self) self.request["USER"] = (POP3_REQ_REJECT) self.request["APOP"] = (POP3_REQ_ACCEPT) Example for converting simple USER/PASS authentication to APOP in POP3 The above example simply rejected USER/PASS authentication, this one converts USER/PASS authentication to APOP authentication messages. class UToAPop3(Pop3Proxy): def config(self): Pop3Proxy.config(self) self.request["USER"] = (POP3_REQ_POLICY,self.DropUSER) self.request["PASS"] = (POP3_REQ_POLICY,self.UToA) def DropUSER(self,command): self.response_value = "+OK" self.response_param = "User ok Send Password" return POP3_REQ_REJECT def UToA(self,command): # Username is stored in self->username, # password in self->request_param, # and the server timestamp in self->timestamp, # consequently the digest can be calculated. # NOTE: This is only an example, calcdigest must be # implemented separately digest = calcdigest(self->timestamp+self->request_param) self->request_command = "APOP" self->request_param = name + " " + digest return POP3_REQ_ACCEPT
Rewriting the banner As in many other protocols, POP3 also starts with a server banner. This banner contains the protocol version the server uses, the possible protocol extensions that it supports and, in many situations, the vendor and exact version number of the POP3 server. This information is useful only if the clients connecting to the POP3 server can be trusted, as it might make bug hunting somewhat easier. On the other hand, this information is also useful for attackers when targeting this service. To prevent this, the banner can be replaced with a neutral one. Use the request hash with the 'GREETING' keyword as shown in the following example. Rewriting the banner in POP3 class NeutralPop3(Pop3Proxy): def config(self): Pop3Proxy.config(self) self.request["GREETING"] = (POP3_REQ_POLICY, None, self.rewriteBanner) def rewriteBanner(self, response) self.response_param = "Pop3 server ready" return POP3_RSP_ACCEPT Some protocol extensions (most notably APOP) use random characters in the greeting message as salt in the authentication process, so changing the banner when APOP is used effectively prevents APOP from working properly.
Stacking The available stacking modes for this proxy module are listed in the following table. For additional information on stacking, see .
Rejecting viruses and spam When filtering messages for viruses or spam, the content vectoring modules reject infected and spam e-mails. In such cases the POP3 proxy notifies the client about the rejected message in a special e-mail. To reject e-mail messages using the ERR protocol element, set the reject_by_mail attribute to FALSE. However, this is not recommended, because several client applications handle ERR responses incorrectly. Infected e-mails are put into the quarantine and deleted from the server.
Related standards Post Office Protocol Version 3 is described in RFC 1939. The POP3 AUTHentication command is described in RFC 1734.
These are in request hashes. POP3_REQ_ACCEPT POP3_REQ_ACCEPT_MLINE POP3_REQ_REJECT POP3_REQ_ABORT POP3_REQ_POLICY These are the pop3 response hashes. POP3_RSP_ACCEPT POP3_RSP_REJECT POP3_RSP_ABORT These are the pop3 proxy stacking capabilities. POP3_STK_NONE POP3_STK_DATA POP3_STK_MIME POP3_STK_POLICY Action codes for POP3 requests Accept the request without any modification. Accept multiline requests without modification. Use it only if unknown commands has to be enabled (i.e. commands not specified in RFC 1939 or RFC 1734). Reject the request. The second parameter contains a string that is sent back to the client. METHOD,METHOD Call the function specified to make a decision about the event. See for details. This action uses two additional tuple items, which must be callable Python functions. The first function receives two parameters: self and command. The second one is called with an answer, (if the answer is multiline, it is called with every line) and receives two parameters: self and response_param. Reject the request and terminate the connection. Action codes for POP3 responses Accept the response without any modification. Reject the response. Reject the response and terminate the connection. Action codes for proxy stacking Call the function specified to decide which part (if any) of the traffic should be passed to the stacked proxy. No additional proxy is stacked into the POP3 proxy. The data part of the traffic including the MIME headers is passed to the specified stacked proxy. Only the data part of the traffic is passed to the specified stacked proxy.
""" from Zorp import * from Proxy import Proxy POP3_REQ_ACCEPT = 1 POP3_REQ_ACCEPT_MLINE = 100 POP3_REQ_REJECT = 3 POP3_REQ_ABORT = 4 POP3_REQ_POLICY = 6 POP3_RSP_ACCEPT = 1 POP3_RSP_REJECT = 3 POP3_RSP_ABORT = 4 POP3_STK_NONE = 1 POP3_STK_DATA = 2 POP3_STK_MIME = 3 POP3_STK_POLICY = 6 class AbstractPop3Proxy(Proxy): """ Class encapsulating the abstract POP3 proxy. This class implements an abstract POP3 proxy - it serves as a starting point for customized proxy classes, but is itself not directly usable. Service definitions should refer to a customized class derived from AbstractPop3Proxy, or a predefined Pop3Proxy proxy class. AbstractPop3Proxy denies all requests by default. timeout 600000 Timeout in milliseconds. If no packet arrives within this interval, connection is dropped. username n/a Username as specified by the client. password Password sent to the server (if any). max_request_line_length 90 Maximum allowed line length for client requests. max_response_line_length 512 Maximum allowed line length for server responses. max_username_length 8 Maximum allowed length of usernames. max_password_length 16 Maximum allowed length of passwords. response_value n/a When a command or response is passed to the policy level, its value can be changed to this value. (It has effect only if the return value is not POP3_*_ACCEPT). response_param n/a When a command or response is passed to the policy level, the value its parameters can be changed to this value. (It has effect only if the return value is not POP3_*_ACCEPT). response_multiline n/a Enable multiline responses. request_command n/a When a command is passed to the policy level, its value can be changed to this value. request_param n/a When a command is passed to the policy level, the value of its parameters can be changed to this value. request Normative policy hash for POP3 requests indexed by the command name (e.g.: "USER", "UIDL", etc.). See also . response_stack Hash containing the stacking policy for multiline POP3 responses. The hash is indexed by the POP3 response. See also . session_timestamp n/a If the POP3 server implements the APOP command, with the greeting message it sends a timestamp, which is stored in this parameter. permit_unknown_command FALSE Enable unknown commands. permit_longline FALSE In multiline answer (especially in downloaded messages) sometimes very long lines can appear. Enabling this option allows the unlimited long lines in multiline answers. max_authline_count 4 Maximum number of lines that can be sent during the authentication conversation. The default value is enough for password authentication, but might have to be increased for other types of authentication. reject_by_mail TRUE If the stacked proxy or content vectoring module rejects an e-mail message, reply with a special e-mail message instead of an ERR response. See for details. """ name = "pop3" def __init__(self, session): """ Initialize a Pop3Proxy instance. Create and set up a Pop3Proxy instance. session SESSION session this instance belongs to """ Proxy.__init__(self, session) class Pop3Proxy(AbstractPop3Proxy): """ Default POP3 proxy based on AbstractPop3Proxy. Pop3Proxy is the default POP3 proxy based on AbstractPop3Proxy, allowing the most commonly used requests. The following requests are permitted: APOP; DELE; LIST; LAST; NOOP; PASS; QUIT; RETR; RSET; STAT; TOP; UIDL; USER; GREETING. All other requests (including CAPA) are rejected. """ def config(self): """ Default config event handler. Enables the most common POP3 methods so we have a useful default configuration. """ self.request["APOP"] = POP3_REQ_ACCEPT self.request["DELE"] = POP3_REQ_ACCEPT self.request["LIST"] = POP3_REQ_ACCEPT self.request["LAST"] = POP3_REQ_ACCEPT self.request["NOOP"] = POP3_REQ_ACCEPT self.request["PASS"] = POP3_REQ_ACCEPT self.request["QUIT"] = POP3_REQ_ACCEPT self.request["RETR"] = POP3_REQ_ACCEPT self.request["RSET"] = POP3_REQ_ACCEPT self.request["STAT"] = POP3_REQ_ACCEPT self.request["TOP"] = POP3_REQ_ACCEPT self.request["UIDL"] = POP3_REQ_ACCEPT self.request["USER"] = POP3_REQ_ACCEPT self.request["CAPA"] = POP3_REQ_REJECT self.request["*"] = POP3_REQ_REJECT self.request["GREETING"] = POP3_REQ_ACCEPT zorp-3.9.5/modules/pop3/messages/000077500000000000000000000000001172670260400166675ustar00rootroot00000000000000zorp-3.9.5/modules/pop3/messages/Makefile.am000066400000000000000000000006271172670260400207300ustar00rootroot00000000000000ERROR_FILES = reject.msg LANGUAGES = en hu EXTRA_DIST = $(addprefix en/,$(ERROR_FILES)) $(addprefix hu/,$(ERROR_FILES)) pkgdatadir = @datadir@/zorp/pop3 install-data-hook: for lang in $(LANGUAGES); do \ test -f $(DESTDIR)$(pkgdatadir)/$$lang || mkdir -p $(DESTDIR)$(pkgdatadir)/$$lang; \ $(INSTALL) -c -m 644 $(addprefix $(srcdir)/$${lang}/,$(ERROR_FILES)) $(DESTDIR)$(pkgdatadir)/$${lang}; \ done zorp-3.9.5/modules/pop3/messages/en/000077500000000000000000000000001172670260400172715ustar00rootroot00000000000000zorp-3.9.5/modules/pop3/messages/en/reject.msg000066400000000000000000000005021172670260400212520ustar00rootroot00000000000000From: @FROM@ To: @TO@ Subject: @SUBJECT@ Content-Type: TEXT/PLAIN Content-Transfer-Encoding: 7bit The original content of this email was rejected by local policy settings. You can recover the original contents of this message from the quarantine. Please consult your administrator for more information @INFO@ zorp-3.9.5/modules/pop3/messages/hu/000077500000000000000000000000001172670260400173035ustar00rootroot00000000000000zorp-3.9.5/modules/pop3/messages/hu/reject.msg000066400000000000000000000005641172670260400212740ustar00rootroot00000000000000From: @FROM@ To: @TO@ Subject: @SUBJECT@ Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ezen levél a tartalma nem felelt meg a helyi biztonsági szabályoknak, ezért visszautasításra került. A levél eredeti tartalma megtalálható a karanténban, vegye fel a kapcsolatot az adminisztrátorral további információért. @INFO@ zorp-3.9.5/modules/pop3/pop3.c000066400000000000000000000621751172670260400161200ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "pop3.h" #include "pop3cmd.h" #include "pop3misc.h" #include "pop3policy.h" #include "pop3data.h" #include #include #include #include #include static void pop3_proxy_free(ZObject *s); static struct _Pop3InternalCommands known_commands[] = { {"APOP", Pop3ParseAPOP, FALSE, Pop3AnswerParseAPOP, NULL, POP3_STATE_AUTH }, {"DELE", Pop3ParseNum_One, FALSE, NULL, NULL, POP3_STATE_TRANS }, {"LIST", Pop3ParseNum_OneOptional, TRUE, NULL, NULL, POP3_STATE_TRANS }, {"LAST", Pop3ParseNoarg, FALSE, Pop3AnswerParseNum_One, NULL, POP3_STATE_TRANS }, {"NOOP", Pop3ParseNoarg, FALSE, NULL, NULL, POP3_STATE_TRANS }, {"PASS", Pop3ParsePASS, FALSE, Pop3AnswerParsePASS, NULL, POP3_STATE_AUTH_U }, {"QUIT", Pop3ParseNoarg, FALSE, Pop3AnswerParseQUIT, NULL, POP3_STATE_AUTH | POP3_STATE_AUTH_U | POP3_STATE_TRANS }, {"RETR", Pop3ParseRETR, TRUE, NULL, NULL, POP3_STATE_TRANS }, {"RSET", Pop3ParseNoarg, FALSE, NULL, NULL, POP3_STATE_TRANS }, {"STAT", Pop3ParseNoarg, FALSE, Pop3AnswerParseNum_Two, NULL, POP3_STATE_TRANS }, {"TOP", Pop3ParseNum_Two, TRUE, NULL, NULL, POP3_STATE_TRANS }, {"UIDL", Pop3ParseNum_OneOptional, TRUE, NULL, NULL, POP3_STATE_TRANS }, {"USER", Pop3ParseUSER, FALSE, Pop3AnswerParseUSER, NULL, POP3_STATE_AUTH }, {"AUTH", Pop3ParseAUTH, FALSE, NULL, NULL, POP3_STATE_AUTH }, {NULL, NULL, FALSE, NULL, NULL, 0 } }; GIOStatus pop3_write_client(Pop3Proxy *self, char *msg) { GIOStatus rc; gsize bytes_written; z_proxy_enter(self); rc = z_stream_write(self->super.endpoints[EP_CLIENT], msg, strlen(msg), &bytes_written, NULL); z_proxy_return(self, rc); } GIOStatus pop3_write_server(Pop3Proxy *self, char *msg) { GIOStatus rc; gsize bytes_written; z_proxy_enter(self); rc = z_stream_write(self->super.endpoints[EP_SERVER], msg, strlen(msg), &bytes_written, NULL); z_proxy_return(self, rc); } gchar * pop3_get_from(gchar *header G_GNUC_UNUSED, gpointer user_data) { Pop3Proxy *self = Z_CAST(user_data, Pop3Proxy); gchar *res; z_proxy_enter(self); res = self->from ? g_strdup(self->from->str) : NULL; z_proxy_return(self, res); } gchar * pop3_get_to(gchar *header G_GNUC_UNUSED, gpointer user_data) { Pop3Proxy *self = Z_CAST(user_data, Pop3Proxy); gchar *res; z_proxy_enter(self); res = self->to ? g_strdup(self->to->str) : NULL; z_proxy_return(self, res); } gchar * pop3_get_subject(gchar *header G_GNUC_UNUSED, gpointer user_data) { Pop3Proxy *self = Z_CAST(user_data, Pop3Proxy); gchar *res; z_proxy_enter(self); res = self->subject ? g_strdup(self->subject->str) : NULL; z_proxy_return(self, res); } static ZErrorLoaderVarInfo pop3_error_vars[] = { {"FROM", pop3_get_from}, {"TO", pop3_get_to}, {"SUBJECT", pop3_get_subject}, {NULL, NULL} }; void pop3_error_msg(Pop3Proxy *self, gchar *additional_info) { gchar *error_msg; gchar error_filename[256]; guint error_len; gchar response[512]; z_proxy_enter(self); g_snprintf(error_filename, sizeof(error_filename), ZORP_DATADIR "/pop3/%s/reject.msg", self->super.language->str); error_msg = z_error_loader_format_file(error_filename, additional_info, Z_EF_ESCAPE_NONE, pop3_error_vars, self); if (error_msg) { error_len = strlen(error_msg); g_snprintf(response, sizeof(response), "+OK %d octets\r\n", error_len); if (pop3_write_client(self, response) != G_IO_STATUS_NORMAL || pop3_write_client(self, error_msg) != G_IO_STATUS_NORMAL) goto exit; if (error_msg[error_len -1] != '\n') { if (pop3_write_client(self, "\r\n") != G_IO_STATUS_NORMAL) goto exit; } } pop3_write_client(self, ".\r\n"); exit: z_proxy_return(self); } GIOStatus pop3_response_read(Pop3Proxy *self) { GIOStatus res; z_proxy_enter(self); self->reply_length = self->max_reply_length; res = z_stream_line_get(self->super.endpoints[EP_SERVER], &self->reply_line, &self->reply_length, NULL); z_proxy_return(self, res); } guint pop3_response_parse(Pop3Proxy *self) { gchar response[5]; guint i; z_proxy_enter(self); if (self->reply_length > self->max_reply_length) { /*LOG This message indicates that the response line is too long and Zorp aborts the connection. Check the 'max_reply_length' attribute. */ z_proxy_log(self, POP3_VIOLATION, 3, "Response line too long; line='%.*s', length='%d', max_reply_length='%d'", (int)self->reply_length, self->reply_line, (int)self->reply_length, self->max_reply_length); z_proxy_return(self, POP3_RSP_ABORT); } for (i = 0; i < 4 && i < self->reply_length && self->reply_line[i] != ' '; i++) response[i] = self->reply_line[i]; response[i++] = 0; if ((strcmp(response,"+OK") != 0) && (strcmp(response,"-ERR") != 0)) { /*LOG This message indicates that the status of the response is invalid and Zorp rejects the response. The response should begin with '+OK' or with '-ERR'. */ z_proxy_log(self, POP3_VIOLATION, 3, "Response status is invalid; rsp='%s'", response); z_proxy_return(self, POP3_RSP_REJECT); } if (strcmp(response, "+OK") != 0) self->response_multiline = FALSE; g_string_assign(self->response, response); if (self->reply_length > i) { g_string_assign_len(self->response_param, self->reply_line + i, self->reply_length - i); /*LOG This message reports that the fetched response contains a parameter. */ z_proxy_log(self, POP3_RESPONSE, 7, "Response fetched with parameter; rsp='%s', rsp_prm='%s'", self->response->str, self->response_param->str); } else { /*LOG This message reports the fetched response. */ z_proxy_log(self, POP3_RESPONSE, 7, "Response fetched; rsp='%s'", response); g_string_assign(self->response_param, ""); } z_proxy_return(self, POP3_RSP_ACCEPT); } guint pop3_response_process(Pop3Proxy *self) { guint ret = POP3_RSP_ACCEPT; z_proxy_enter(self); if (self->pop3_state == POP3_STATE_LISTEN) { pop3_get_timestamp(self); self->pop3_state = POP3_STATE_AUTH; } ret = pop3_policy_response_hash_do(self); if (ret == POP3_RSP_ACCEPT) { if (self->command_desc && self->command_desc->response_parse) ret = self->command_desc->response_parse(self); } z_proxy_return(self, ret); } void pop3_response_write(Pop3Proxy *self) { gchar newline[self->max_reply_length + 3]; z_proxy_enter(self); if (self->response_param->len) g_snprintf(newline, sizeof(newline), "%s %s\r\n", self->response->str, self->response_param->str); else g_snprintf(newline, sizeof(newline), "%s\r\n", self->response->str); pop3_write_client(self, newline); z_proxy_return(self); } void pop3_response_reject(Pop3Proxy *self, gchar *error_msg) { gchar msg_buf[1024]; z_proxy_enter(self); if (!error_msg) error_msg = "Error in protocol"; g_snprintf(msg_buf, sizeof(msg_buf), "-ERR %s\r\n", error_msg); pop3_write_client(self, msg_buf); z_proxy_return(self); } gboolean pop3_response_multiline(Pop3Proxy *self) { gboolean res = TRUE; z_proxy_enter(self); res = pop3_data_transfer(self); if (!res) { /*LOG This message indicates that the multi-line data transfer failed and Zorp rejects the response. */ z_proxy_log(self, POP3_ERROR, 2, "Data transfer failed;"); } self->state = POP3_CLIENT; z_proxy_return(self, res); } gboolean pop3_server_to_client(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { Pop3Proxy *self = (Pop3Proxy *)user_data; guint resp; GIOStatus rc; z_proxy_enter(self); rc = pop3_response_read(self); if (rc != G_IO_STATUS_NORMAL) { if (rc != G_IO_STATUS_EOF) pop3_response_reject(self, NULL); self->pop3_state = POP3_STATE_QUIT; z_proxy_return(self, FALSE); } if (self->pop3_state != POP3_STATE_AUTH_A && self->pop3_state != POP3_STATE_AUTH_A_CANCEL) { resp = pop3_response_parse(self); if (resp == POP3_RSP_ACCEPT) resp = pop3_response_process(self); } else { resp = pop3_auth_parse(self, EP_SERVER); } switch (resp) { case POP3_RSP_ACCEPT: /* This is a dirty hack. * Because sources not recurses, we must leave * z_poll_iter_timeout before z_transfer_run called */ if (self->response_multiline) { self->state = POP3_SERVER_MULTILINE; z_proxy_return(self, TRUE); } else { pop3_response_write(self); } break; case POP3_RSP_REJECT: pop3_response_reject(self, NULL); break; case POP3_RSP_ABORT: pop3_response_reject(self, NULL); self->pop3_state = POP3_STATE_QUIT; break; default: self->pop3_state = POP3_STATE_QUIT; break; } self->state = POP3_CLIENT; z_proxy_return(self, TRUE); } void pop3_command_reject(Pop3Proxy *self); gboolean pop3_command_read(Pop3Proxy *self) { GIOStatus res; z_proxy_enter(self); self->response_multiline = FALSE; self->request_length = self->max_request_length; res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &self->request_line, &self->request_length, NULL); if (res !=G_IO_STATUS_NORMAL) { /* FIXMEE Check if streamline return with error because of too long line, or connection broken. */ if (res != G_IO_STATUS_EOF) pop3_command_reject(self); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } guint pop3_command_parse(Pop3Proxy *self) { gchar command[10]; guint i; z_proxy_enter(self); if (self->request_length > self->max_request_length) { /*LOG This message indicates that the request line is too long and Zorp rejects the request. Check the 'max_request_length' attribute. */ z_proxy_log(self, POP3_VIOLATION, 3, "Request line too long; line='%.*s', length='%d', max_request_length='%d'", (int)self->request_length, self->request_line, (int)self->request_length, self->max_request_length); z_proxy_return(self, POP3_REQ_ABORT); } for(i = 0; i < sizeof(command) - 1 && i < self->request_length && self->request_line[i] != ' '; i++) command[i] = self->request_line[i]; command[i++] = 0; g_string_assign(self->command, command); g_string_up(self->command); if (self->request_length > i) { g_string_assign_len(self->command_param, self->request_line + i, self->request_length - i); /*LOG This message reports that the fetched request contains a parameter. */ z_proxy_log(self, POP3_REQUEST, 7, "Request fetched with parameter; req='%s', req_prm='%s'", self->command->str, self->command_param->str); } else { /*LOG This message reports the fetched request. */ z_proxy_log(self, POP3_REQUEST, 7, "Request fetched; req='%s'", self->command->str); g_string_assign(self->command_param, ""); } self->command_desc = g_hash_table_lookup(self->pop3_commands, self->command->str); if (!self->command_desc && !self->permit_unknown_command && !pop3_policy_command_hash_search(self, self->command->str)) { /*LOG This message indicates that the request was unknown and Zorp aborts the connection. Check the 'permit_unknown_command' and the 'request' attributes. */ z_proxy_log(self, POP3_REQUEST, 3, "Unknown request command; req='%s'", self->command->str); z_proxy_return(self, POP3_REQ_ABORT); } if (self->command_desc && !(self->command_desc->pop3_state & self->pop3_state)) { /*LOG This message indicates that the request command is not allowed in this state of the protocol and Zorp rejects the request. */ z_proxy_log(self, POP3_REQUEST, 3, "Request command not allowed in this state; req='%s', state='%d'", self->command->str, self->pop3_state); z_proxy_return(self, POP3_REQ_REJECT); } z_proxy_return(self, POP3_REQ_ACCEPT); } guint pop3_command_process(Pop3Proxy *self) { guint res = POP3_REQ_ACCEPT; z_proxy_enter(self); res = pop3_policy_command_hash_do(self); if (res == POP3_REQ_ACCEPT) { if (self->command_desc) { self->response_multiline = self->command_desc->multi_line_response; if (self->command_desc->command_parse) res = self->command_desc->command_parse(self); } } z_proxy_return(self, res); } void pop3_command_write(Pop3Proxy *self) { gchar newline[self->max_request_length + 3]; z_proxy_enter(self); if (self->command_param->len > 0) g_snprintf(newline, sizeof(newline), "%s %s\r\n", self->command->str, self->command_param->str); else g_snprintf(newline, sizeof(newline), "%s\r\n", self->command->str); pop3_write_server(self, newline); z_proxy_return(self); } void pop3_command_reject(Pop3Proxy *self) { gchar newline[self->max_reply_length + 1]; z_proxy_enter(self); g_snprintf(newline, sizeof(newline), "%s %s\r\n", self->response->str, self->response_param->str); pop3_write_client(self, newline); z_proxy_return(self); } gboolean pop3_client_to_server(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { Pop3Proxy *self = (Pop3Proxy *)user_data; guint resp; z_proxy_enter(self); g_string_assign(self->response, "-ERR"); g_string_assign(self->response_param, "Invalid command."); resp = pop3_command_read(self); if (!resp) { self->pop3_state = POP3_STATE_QUIT; z_proxy_return(self, FALSE); } /* NOTE * POP3_STATE_AUTH_A_CANCEL state not needed because * it's could only be set after this and no other turn * available */ if (self->pop3_state != POP3_STATE_AUTH_A) { resp = pop3_command_parse(self); if (resp == POP3_REQ_ACCEPT) resp = pop3_command_process(self); } else { resp = pop3_auth_parse(self, EP_CLIENT); } switch (resp) { case POP3_REQ_ACCEPT: pop3_command_write(self); self->state = POP3_SERVER; break; case POP3_REQ_REJECT: pop3_command_reject(self); break; case POP3_REQ_ABORT: pop3_command_reject(self); /* No break ! */ default: self->pop3_state = POP3_STATE_QUIT; break; } z_proxy_return(self, TRUE); } void pop3_set_defaults(Pop3Proxy *self) { z_proxy_enter(self); self->max_username_length = 32; self->max_password_length = 32; self->username = g_string_new(""); self->password = g_string_new(""); self->command = g_string_new(""); self->command_param = g_string_new(""); self->response = g_string_new(""); self->response_param = g_string_new(""); self->timestamp = g_string_new(""); self->timeout = 600000; self->max_request_length = 90; self->max_reply_length = 512; self->pop3_commands = g_hash_table_new(g_str_hash, g_str_equal); self->commands_policy = g_hash_table_new(g_str_hash, g_str_equal); self->command_stack = g_hash_table_new(g_str_hash, g_str_equal); self->command_desc = NULL; self->policy_enable_longline = TRUE; self->buffer_length = 4096; self->max_authline_count = 4; self->reject_by_mail = TRUE; z_proxy_leave(self); } void pop3_register_vars(Pop3Proxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "timeout", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->timeout); z_proxy_var_new(&self->super, "max_request_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_request_length); z_proxy_var_new(&self->super, "max_response_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_reply_length); z_proxy_var_new(&self->super, "max_username_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_username_length); z_proxy_var_new(&self->super, "max_password_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_password_length); z_proxy_var_new(&self->super, "max_authline_count", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_authline_count); z_proxy_var_new(&self->super, "username", Z_VAR_TYPE_STRING | Z_VAR_GET, self->username); z_proxy_var_new(&self->super, "password", Z_VAR_TYPE_STRING | Z_VAR_GET, self->password); z_proxy_var_new(&self->super, "request_command", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->command); z_proxy_var_new(&self->super, "request_param", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->command_param); z_proxy_var_new(&self->super, "response_value", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->response); z_proxy_var_new(&self->super, "response_param", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->response_param); z_proxy_var_new(&self->super, "session_timestamp", Z_VAR_TYPE_STRING | Z_VAR_GET, self->timestamp); z_proxy_var_new(&self->super, "response_multiline", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET, &self->response_multiline); z_proxy_var_new(&self->super, "permit_unknown_command", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET, &self->permit_unknown_command); z_proxy_var_new(&self->super, "request", Z_VAR_TYPE_HASH | Z_VAR_GET_CONFIG | Z_VAR_GET, self->commands_policy); z_proxy_var_new(&self->super, "response_stack", Z_VAR_TYPE_HASH | Z_VAR_GET_CONFIG | Z_VAR_GET, self->command_stack); z_proxy_var_new(&self->super, "permit_longline", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->policy_enable_longline); z_proxy_var_new(&self->super, "buffer_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->buffer_length); z_proxy_var_new(&self->super, "reject_by_mail", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->reject_by_mail); z_proxy_return(self); } guint pop3_init_streams(Pop3Proxy *self) { ZStream *tmpstream; z_proxy_enter(self); if (!self->super.endpoints[EP_SERVER] || !self->super.endpoints[EP_CLIENT]) z_proxy_return(self, FALSE); self->super.endpoints[EP_CLIENT]->timeout = self->timeout; self->super.endpoints[EP_SERVER]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_CLIENT]; self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->buffer_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); tmpstream = self->super.endpoints[EP_SERVER]; self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->buffer_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_stream_set_callback(self->super.endpoints[EP_CLIENT], G_IO_IN, pop3_client_to_server, self, NULL); z_stream_set_callback(self->super.endpoints[EP_SERVER], G_IO_IN, pop3_server_to_client, self, NULL); z_poll_add_stream(self->poll, self->super.endpoints[EP_CLIENT]); z_poll_add_stream(self->poll, self->super.endpoints[EP_SERVER]); z_proxy_return(self, TRUE); } static void pop3_deinit_streams(Pop3Proxy *self) { z_poll_remove_stream(self->poll, self->super.endpoints[EP_SERVER]); z_poll_remove_stream(self->poll, self->super.endpoints[EP_CLIENT]); } void pop3_config_init(Pop3Proxy *self) { int i; z_proxy_enter(self); /* Load the command hash. */ for (i = 0; known_commands[i].name != NULL; i++) g_hash_table_insert(self->pop3_commands, known_commands[i].name, &known_commands[i]); if (self->max_request_length + 1 > self->buffer_length) self->buffer_length = self->max_request_length + 1; if (self->max_reply_length + 1 > self->buffer_length) self->buffer_length = self->max_request_length + 1; self->poll = z_poll_new(); z_proxy_return(self); } static gboolean pop3_config(ZProxy *s) { Pop3Proxy *self = Z_CAST(s, Pop3Proxy); z_proxy_enter(self); pop3_set_defaults(self); pop3_register_vars(self); if (Z_SUPER(s, ZProxy)->config(s)) { pop3_config_init(self); z_proxy_return(self, TRUE); } z_proxy_return(self, FALSE); } /** * pop3_main: * s: Pop3Proxy instance * * Proxy main function. **/ static void pop3_main(ZProxy *s) { Pop3Proxy *self = (Pop3Proxy *) s; z_proxy_enter(self); if (!z_proxy_connect_server(&self->super, NULL, 0) || !pop3_init_streams(self)) z_proxy_return(self); self->pop3_state = POP3_STATE_LISTEN; self->state = POP3_SERVER; z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, FALSE); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, TRUE); while (self->pop3_state != POP3_STATE_QUIT && z_poll_is_running(self->poll)) { if (!z_proxy_loop_iteration(s)) { self->pop3_state = POP3_STATE_QUIT; break; } switch(self->state) { case POP3_CLIENT: z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, TRUE); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, FALSE); break; case POP3_SERVER: z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, FALSE); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, TRUE); break; case POP3_SERVER_MULTILINE: pop3_response_multiline(self); continue; default: self->pop3_state = POP3_STATE_QUIT; break; } if (!z_poll_iter_timeout(self->poll, self->timeout)) self->pop3_state = POP3_STATE_QUIT; } pop3_deinit_streams(self); z_proxy_return(self); } ZProxyFuncs pop3_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), pop3_proxy_free, }, .config = pop3_config, .main = pop3_main, NULL }; Z_CLASS_DEF(Pop3Proxy, ZProxy, pop3_proxy_funcs); static ZProxy * pop3_proxy_new(ZProxyParams *params) { Pop3Proxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(Pop3Proxy), params), Pop3Proxy); z_return((ZProxy *) self); } static void pop3_proxy_free(ZObject *s) { Pop3Proxy *self = (Pop3Proxy *) s; z_enter(); g_hash_table_destroy(self->pop3_commands); z_poll_unref(self->poll); z_proxy_free_method(s); z_return(); } /*+ Module initialization function. Registers a new proxy type. +*/ gint zorp_module_init(void) { z_registry_add("pop3", ZR_PROXY, pop3_proxy_new); return TRUE; } zorp-3.9.5/modules/pop3/pop3.h000066400000000000000000000112521172670260400161130ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_POP3_H_INCLUDED #define ZORP_MODULES_POP3_H_INCLUDED #include #include #include #include #include #include #include #define POP3_DEBUG "pop3.debug" #define POP3_REQUEST "pop3.request" #define POP3_RESPONSE "pop3.response" #define POP3_ERROR "pop3.error" #define POP3_VIOLATION "pop3.violation" #define POP3_POLICY "pop3.policy" #define POP3_STATE_LISTEN 0 #define POP3_STATE_AUTH 1 #define POP3_STATE_AUTH_U 2 #define POP3_STATE_AUTH_A 4 #define POP3_STATE_AUTH_A_CANCEL 8 #define POP3_STATE_TRANS 16 #define POP3_STATE_QUIT 32 #define POP3_SERVER 0 #define POP3_CLIENT 1 #define POP3_SERVER_MULTILINE 2 #define POP3_SERVER_MULTILINE_DOT 3 #define POP3_SERVER_MULTILINE_DROP 4 #define POP3_REQ_ACCEPT 1 #define POP3_REQ_REJECT 3 #define POP3_REQ_ABORT 4 #define POP3_REQ_POLICY 6 #define POP3_REQ_ACCEPT_MLINE 100 #define POP3_RSP_ACCEPT 1 #define POP3_RSP_REJECT 3 #define POP3_RSP_ABORT 4 #define POP3_STK_NONE 1 #define POP3_STK_DATA 2 #define POP3_STK_MIME 3 #define POP3_STK_POLICY 6 struct _Pop3Proxy; struct _Pop3InternalCommands; typedef guint (*Pop3CmdFunction)(struct _Pop3Proxy *); typedef struct _Pop3InternalCommands { gchar *name; Pop3CmdFunction command_parse; gboolean multi_line_response; Pop3CmdFunction response_parse; Pop3CmdFunction response_multiline_parse; guint pop3_state; } Pop3InternalCommands; typedef struct _Pop3Proxy { ZProxy super; gint timeout; /* Timeout value in milisec */ gboolean policy_enable_longline; /* With this switch you may disable long line */ guint max_username_length; /* Max acceptable username length */ guint max_password_length; /* Max acceptable password length */ GHashTable *commands_policy; /* Command normative hash */ GHashTable *command_stack; /* What to stack for this command */ guint max_request_length; /* Max length of a client request */ guint max_reply_length; /* Max length of a server response */ gboolean permit_unknown_command; /* Permit commands not known to proxy */ guint buffer_length; /* Length of read buffer */ guint max_authline_count; /* maximum number of auth lines */ gboolean reject_by_mail; /* If stacked proxy reject, reject it with error or a speical formatted mail */ /* State of pop3 session */ gint pop3_state; /* State of Pop3 Proxy */ gint state; GString *username; GString *password; GString *command; GString *command_param; GString *response; GString *response_param; gboolean response_multiline; gchar *request_line; gchar *reply_line; gsize request_length; gsize reply_length; GHashTable *pop3_commands; Pop3InternalCommands *command_desc; GString *timestamp; ZStackedProxy *stacked; ZPoll *poll; /* This variable holds the number of arrived auth lines */ guint auth_lines; GString *from; GString *to; GString *subject; } Pop3Proxy; extern ZClass Pop3Proxy__class; /* pop3.c */ void pop3_response_reject(Pop3Proxy *self, gchar *error_msg); void pop3_response_write(Pop3Proxy *self); GIOStatus pop3_write_client(Pop3Proxy *self, char *msg); void pop3_error_msg(Pop3Proxy *self, gchar *additional_info); /* pop3auth.c */ guint pop3_auth_parse(Pop3Proxy *self, guint side); #endif zorp-3.9.5/modules/pop3/pop3auth.c000066400000000000000000000057311172670260400167750ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "pop3.h" guint pop3_auth_parse(Pop3Proxy *self, guint side) { z_proxy_enter(self); self->auth_lines++; if (side == EP_CLIENT) { g_string_assign_len(self->command, self->request_line, self->request_length); g_string_assign(self->command_param, ""); if (self->request_line[0] == '*' && self->request_length == 1) { self->pop3_state = POP3_STATE_AUTH_A_CANCEL; z_proxy_return(self, POP3_RSP_ACCEPT); } } else if (side == EP_SERVER) { g_string_assign_len(self->response, self->reply_line, self->reply_length); g_string_assign(self->response_param, ""); if (strstr(self->response->str, "+OK ") == self->response->str && self->pop3_state != POP3_STATE_AUTH_A_CANCEL) { self->pop3_state = POP3_STATE_TRANS; z_proxy_return(self, POP3_RSP_ACCEPT); } else if (strstr(self->response->str, "-ERR ") == self->response->str) { self->pop3_state = POP3_STATE_AUTH; z_proxy_return(self, POP3_RSP_ACCEPT); } else if (self->response->len < 3 || self->response->str[0] != '+' || self->response->str[1] != ' ') { z_proxy_return(self, POP3_RSP_ABORT); } else if (self->pop3_state == POP3_STATE_AUTH_A_CANCEL) { z_proxy_log(self, POP3_VIOLATION, 2, "Auth cancellation must be followed with -ERR; line='%s'", self->response->str); g_string_assign(self->response, "-ERR Error in protocol"); z_proxy_return(self, POP3_RSP_ABORT); } } if (self->auth_lines > self->max_authline_count) { self->pop3_state = POP3_STATE_AUTH; z_proxy_return(self, POP3_REQ_REJECT); } z_proxy_return(self, POP3_REQ_ACCEPT); } zorp-3.9.5/modules/pop3/pop3cmd.c000066400000000000000000000412421172670260400165740ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "pop3.h" guint Pop3ParseNoarg(Pop3Proxy *self) { z_proxy_enter(self); if (self->command_param->len > 0) /*LOG This message indicates that the request must not have any parameter and Zorp is going to drop the parameter. */ z_proxy_log(self, POP3_REQUEST, 4, "Dropping request parameter, no parameter allowed; req='%s', req_prm='%s'", self->command->str, self->command_param->str); g_string_assign(self->command_param, ""); z_proxy_return(self, POP3_REQ_ACCEPT); } guint Pop3ParseNum_One(Pop3Proxy *self) { long int arg; gchar *err; z_proxy_enter(self); arg = strtol(self->command_param->str, &err, 10); if (err == self->command_param->str) { /*LOG This message indicates that the numerical parameter of the request is missing and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The required numerical parameter of the request is missing; req='%s' req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (errno == ERANGE) { /*LOG This message indicates that the numerical parameter of the request is not in the given range and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The numerical parameter of the request is not in the given range; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (arg < 0) { /*LOG This message indicates that the numerical parameter of the request is a negative number which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The numerical parameter of the request is negative; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (arg == 0) { /*LOG This message indicates that the numerical parameter of the request is zero which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The numerical parameter of the request is zero; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (*err != 0) { /*LOG This message indicates that the numerical parameter of the request contains junk characters after the number but Zorp ignores and truncates the junk. */ z_proxy_log(self, POP3_REQUEST, 4, "The numerical parameter of the request contains junk after the number; req='%s', req_prm='%s'", self->command->str, self->command_param->str); } g_string_printf(self->command_param, "%ld", arg); z_proxy_return(self, POP3_REQ_ACCEPT); } guint Pop3ParseNum_OneOptional(Pop3Proxy *self) { guint ret; z_proxy_enter(self); if (strlen(self->command_param->str) == 0) z_proxy_return(self, POP3_REQ_ACCEPT); self->response_multiline = FALSE; ret = Pop3ParseNum_One(self); z_proxy_return(self, ret); } guint Pop3ParseNum_Two(Pop3Proxy *self) { long int arg1, arg2; gchar *err = NULL; gchar *next; gchar newline[self->max_reply_length]; z_proxy_enter(self); arg1 = strtol(self->command_param->str, &err, 10); if (errno == ERANGE) { /*LOG This message indicates that the first numerical parameter is not in the given range and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The first numerical parameter of the request is not in the given range; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (arg1 < 0) { /*LOG This message indicates that the first numerical parameter of the request is a negative number which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The first numerical parameter of the request is negative; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } next = err; err = NULL; if (*next == 0) { /*LOG This message indicates that only one numerical parameter is present but two is required and Zorp rejects the request. */ z_proxy_log(self, POP3_REQUEST, 3, "Only one numerical argument in request; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_REJECT); } arg2 = strtol(next, &err, 10); if (errno == ERANGE) { /*LOG This message indicates that the second numerical parameter is not in the given range and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The second numerical parameter of the request is not in the given range; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (arg2 < 0) { /*LOG This message indicates that the second numerical parameter of the request is a negative number which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_REQUEST, 3, "The second numerical parameter of the request is a negative number; req='%s', req_prm='%s'", self->command->str, self->command_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (*err != 0) { /*LOG This message indicates that the numerical parameters of the request contain junk characters after the numbers but Zorp ignores and truncates the junk. */ z_proxy_log(self, POP3_REQUEST, 4, "The numerical parameter of the request contain junk after the number; req='%s', req_prm='%s'", self->command->str, self->command_param->str); } g_snprintf(newline, sizeof(newline), "%ld %ld", arg1, arg2); g_string_assign(self->command_param, newline); z_proxy_return(self, POP3_REQ_ACCEPT); } guint Pop3ParseRETR(Pop3Proxy *self) { guint ret; z_proxy_enter(self); ret = Pop3ParseNum_One(self); z_proxy_return(self, ret); } guint Pop3ParseUSER(Pop3Proxy *self) { gchar username[self->max_username_length + 1]; z_proxy_enter(self); if (self->command_param->len <= self->max_username_length) { g_strlcpy(username, self->command_param->str, self->max_username_length + 1); g_string_assign(self->username, username); z_proxy_return(self, POP3_REQ_ACCEPT); } /*LOG This message indicates that the username parameter of the request is too long and Zorp rejects the request. Check the 'max_username_length' attribute. */ z_proxy_log(self, POP3_POLICY, 2, "Username is too long; max_username_length='%d', username_length='%" G_GSIZE_FORMAT "', username='%s'", self->max_username_length, self->command_param->len, self->command_param->str); z_proxy_return(self, POP3_REQ_REJECT); } guint Pop3ParsePASS(Pop3Proxy *self) { gchar password[self->max_password_length + 1]; z_proxy_enter(self); if (self->command_param->len <= self->max_password_length) { g_strlcpy(password, self->command_param->str, self->max_password_length + 1); g_string_assign(self->password, password); z_proxy_return(self, POP3_REQ_ACCEPT); } /*LOG This message indicates that the password parameter of the request is too long and Zorp rejects the request. Check the 'max_password_length' attribute. */ z_proxy_log(self, POP3_POLICY, 2, "Password is too long; max_password_length='%d', password_length='%d'", self->max_password_length, (gint) self->command_param->len); z_proxy_return(self, POP3_REQ_REJECT); } guint Pop3ParseAPOP(Pop3Proxy *self) { gchar username[self->max_username_length + 1]; guint i; gchar *buf = self->command_param->str; z_proxy_enter(self); for (i = 0; i < self->max_username_length && buf[i] != 0 && buf[i] != ' '; i++) username[i] = buf[i]; username[i] = 0; if (buf[i] != ' ') { /*LOG This message indicates that the username parameter is too long or the digest missing after the username and Zorp rejects the request. */ z_proxy_log(self, POP3_REQUEST, 3, "The username parameter is too long or the digest parameter is missing; req='APOP', req_prm='%s'", self->command_param->str); z_proxy_return(self, POP3_REQ_REJECT); } g_string_assign(self->username, username); while (buf[i] == 32) i++; buf = &buf[i]; for (i = 0; i < 32 && buf[i] != 0 && ((buf[i] >= '0' && buf[i] <= '9') || (buf[i] >= 'a' && buf[i] <= 'f') || (buf[i] >= 'A' && buf[i] <= 'F')); i++) ; if (i < 32) { /*LOG This message indicates that the MD5 digest parameter of the request is invalid and Zorp rejects the request. */ z_proxy_log(self, POP3_REQUEST, 3, "Error parsing the MD5 digest; req='APOP', req_prm='%s'", self->command_param->str); z_proxy_return(self, POP3_REQ_REJECT); } z_proxy_return(self, POP3_REQ_ACCEPT); } guint Pop3AnswerParseUSER(Pop3Proxy *self) { z_proxy_enter(self); if (strcmp(self->response->str, "+OK") == 0) self->pop3_state = POP3_STATE_AUTH_U; z_proxy_return(self, POP3_RSP_ACCEPT); } guint Pop3AnswerParsePASS(Pop3Proxy *self) { z_proxy_enter(self); if (strcmp(self->response->str, "+OK") == 0) self->pop3_state = POP3_STATE_TRANS; else self->pop3_state = POP3_STATE_AUTH; z_proxy_return(self, POP3_RSP_ACCEPT); } guint Pop3AnswerParseAPOP(Pop3Proxy *self) { z_proxy_enter(self); if (strcmp(self->response->str, "+OK") == 0) self->pop3_state = POP3_STATE_TRANS; else self->pop3_state = POP3_STATE_AUTH; z_proxy_return(self, POP3_RSP_ACCEPT); } guint Pop3AnswerParseQUIT(Pop3Proxy *self) { z_proxy_enter(self); self->pop3_state = POP3_STATE_QUIT; z_proxy_return(self, POP3_RSP_ACCEPT); } guint Pop3ParseAUTH(Pop3Proxy *self) { z_proxy_enter(self); self->pop3_state = POP3_STATE_AUTH_A; self->auth_lines = 0; z_proxy_return(self, POP3_RSP_ACCEPT); } guint Pop3AnswerParseNum_One(Pop3Proxy *self) { long int arg; gchar *err; gchar newline[self->max_reply_length]; z_proxy_enter(self); if (!strcmp(self->response->str, "-ERR")) z_proxy_return(self, POP3_RSP_ACCEPT); arg = strtol(self->response_param->str, &err, 10); if ( err == self->response_param->str ) { /*LOG This message indicates that the numerical parameter of the response is missing and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The required numerical parameter of the response is missing; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } if (errno == ERANGE) { /*LOG This message indicates that the numerical parameter of the response is not in the given range and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The numerical parameter of the response is not in the given range; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } if (arg < 0) { /*LOG This message indicates that the numerical parameter of the response is a negative number which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The numerical parameter of the response is a negative number; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } if (*err != 0) { /*LOG This message indicates that the numerical parameter of the response contains junk characters after the number but Zorp ignores and truncates the junk. */ z_proxy_log(self, POP3_RESPONSE, 4, "The numerical parameter of the response contains junk after the number; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); } g_snprintf(newline, sizeof(newline), "%ld", arg); g_string_assign(self->response_param, newline); z_proxy_leave(self); return POP3_RSP_ACCEPT; } guint Pop3AnswerParseNum_Two(Pop3Proxy *self) { long int arg1, arg2; gchar *err = NULL; gchar *next; gchar newline[self->max_reply_length]; z_proxy_enter(self); if (!strcmp(self->response->str, "-ERR")) z_proxy_return(self, POP3_RSP_ACCEPT); arg1 = strtol(self->response_param->str, &err, 10); if (errno == ERANGE) { /*LOG This message indicates that the numerical parameter of the response is not in the given range and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The numerical parameter of the response is not in the given range; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } if (err == self->response_param->str) { /*LOG This message indicates that the numerical parameter of the response is missing and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The required numerical parameter of the response is missing; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (arg1 < 0) { /*LOG This message indicates that the numerical parameter of the response is a negative number which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The numerical parameter of the response is a negative number; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } next = err; err = NULL; arg2 = strtol(next, &err, 10); if (errno == ERANGE) { /*LOG This message indicates that the second numerical parameter of the response is not in the given range and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The second numerical parameter of the response is not in the given range; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } if (err == next) { /*LOG This message indicates that the second numerical parameter of the response is missing and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The required second numerical parameter of the response is missing; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_REQ_ABORT); } if (arg2 < 0) { /*LOG This message indicates that the second numerical parameter of the response is a negative number which is invalid and Zorp aborts the connection. */ z_proxy_log(self, POP3_RESPONSE, 3, "The second numerical parameter of the response is a negative number; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); z_proxy_return(self, POP3_RSP_ABORT); } if (*err != 0) { /*LOG This message indicates that the second numerical parameter of the response contains junk characters after the number but Zorp ignores and truncates the junk. */ z_proxy_log(self, POP3_REQUEST, 4, "The second numerical parameter of the response contains junk after the number; req='%s', rsp_prm='%s'", self->command->str, self->response_param->str); } g_snprintf(newline, sizeof(newline), "%ld %ld", arg1, arg2); g_string_assign(self->response_param, newline); z_proxy_return(self, POP3_RSP_ACCEPT); } zorp-3.9.5/modules/pop3/pop3cmd.h000066400000000000000000000040211172670260400165730ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_MODULES_POP3CMD_H_INCLUDED #define ZORP_MODULES_POP3CMD_H_INCLUDED guint Pop3ParseNoarg(Pop3Proxy *self); guint Pop3ParseNum_One(Pop3Proxy *self); guint Pop3ParseNum_OneOptional(Pop3Proxy *self); guint Pop3ParseLIST(Pop3Proxy *self); guint Pop3ParseNum_Two(Pop3Proxy *self); guint Pop3ParseUSER(Pop3Proxy *self); guint Pop3ParsePASS(Pop3Proxy *self); guint Pop3ParseAPOP(Pop3Proxy *self); guint Pop3ParseAUTH(Pop3Proxy *self); guint Pop3ParseRETR(Pop3Proxy *self); guint Pop3AnswerParseUSER(Pop3Proxy *self); guint Pop3AnswerParsePASS(Pop3Proxy *self); guint Pop3AnswerParseAPOP(Pop3Proxy *self); guint Pop3AnswerParseQUIT(Pop3Proxy *self); guint Pop3AnswerParseAUTH(Pop3Proxy *self); guint Pop3AnswerParseNum_One(Pop3Proxy *self); guint Pop3AnswerParseNum_Two(Pop3Proxy *self); #endif zorp-3.9.5/modules/pop3/pop3data.c000066400000000000000000000176661172670260400167570ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "pop3data.h" #include "pop3policy.h" #include typedef struct _Pop3Transfer { ZDotTransfer super; gint header_state; GString *actual_header; } Pop3Transfer; extern ZClass Pop3Transfer__class; static gboolean pop3_transfer_stack(ZTransfer2 *s, ZStackedProxy **stacked) { Pop3Proxy *owner = Z_CAST(s->owner, Pop3Proxy); return pop3_policy_stack_hash_do(owner, stacked); } enum { POP3_HEADER_NONE, POP3_HEADER_INSIDE, POP3_HEADER_END }; #define FROM "From: " #define TO "To: " #define SUBJECT "Subject: " static GIOStatus pop3_transfer_src_read(ZTransfer2 *s, ZStream *stream, gchar *buf, gsize count, gsize *bytes_read, GError **err) { GIOStatus ret; Pop3Transfer *self = Z_CAST(s, Pop3Transfer); Pop3Proxy *owner = Z_CAST(s->owner, Pop3Proxy); ret = Z_SUPER(s, ZTransfer2)->src_read(s, stream, buf, count, bytes_read, err); if (self->header_state < POP3_HEADER_END && (ret == G_IO_STATUS_NORMAL || (ret == G_IO_STATUS_AGAIN && *bytes_read > 0))) { if (*bytes_read == 0) { self->header_state = POP3_HEADER_END; } else { gsize bytes_need = *bytes_read; if (buf[0] != ' ' && buf[0] != '\t') { self->header_state = POP3_HEADER_NONE; self->actual_header = NULL; } while (buf[bytes_need - 1] == '\n' || buf[bytes_need - 1] == '\r' || buf[bytes_need - 1] == ' ') bytes_need--; z_trace(NULL, "Read header line; line='%.*s'", (gint) bytes_need, buf); if (self->header_state == POP3_HEADER_NONE) { if (g_ascii_strncasecmp(buf, FROM, MIN(__builtin_strlen(FROM), bytes_need)) == 0) { if (owner->from == NULL) { self->header_state = POP3_HEADER_INSIDE; owner->from = g_string_new_len(buf + __builtin_strlen(FROM), bytes_need - __builtin_strlen(FROM)); self->actual_header = owner->from; } else { /* FIXME: Log */ } } else if (g_ascii_strncasecmp(buf, TO, MIN(__builtin_strlen(TO), bytes_need)) == 0) { if (owner->to == NULL) { self->header_state = POP3_HEADER_INSIDE; owner->to = g_string_new_len(buf + __builtin_strlen(TO), bytes_need - __builtin_strlen(TO)); self->actual_header = owner->to; } else { /* FIXME: Log */ } } else if (g_ascii_strncasecmp(buf, SUBJECT, MIN(__builtin_strlen(SUBJECT), bytes_need)) == 0) { if (owner->subject == NULL) { self->header_state = POP3_HEADER_INSIDE; owner->subject = g_string_new_len(buf + __builtin_strlen(SUBJECT), bytes_need - __builtin_strlen(SUBJECT)); self->actual_header = owner->subject; } else { /* FIXME: Log */ } } } else { g_string_append(self->actual_header, "\r\n"); g_string_append_len(self->actual_header, buf, bytes_need); } } } return ret; } ZTransfer2Funcs pop3_transfer_funcs = { { Z_FUNCS_COUNT(ZTransfer2), NULL, }, .src_read = pop3_transfer_src_read, .dst_write = NULL, .src_shutdown = NULL, .dst_shutdown = NULL, .stack_proxy = pop3_transfer_stack, .setup = NULL, /* setup */ .run = NULL, .progress = NULL /* progress */ }; Z_CLASS_DEF(Pop3Transfer, ZDotTransfer, pop3_transfer_funcs); gboolean pop3_data_transfer(Pop3Proxy *owner) { Pop3Transfer *t; GString *preamble; gboolean success; gchar buf[256]; z_proxy_enter(owner); preamble = g_string_new(owner->response->str); if (owner->response_param->len) { g_string_append_c(preamble, ' '); g_string_append(preamble, owner->response_param->str); } g_string_append(preamble, "\r\n"); t = Z_CAST(z_dot_transfer_new(Z_CLASS(Pop3Transfer), &owner->super, owner->poll, owner->super.endpoints[EP_SERVER], owner->super.endpoints[EP_CLIENT], owner->buffer_length, owner->timeout, ZT2F_COMPLETE_COPY | ZT2F_PROXY_STREAMS_POLLED, preamble), Pop3Transfer); z_transfer2_set_content_format(&t->super.super, "email"); z_stream_line_set_nul_nonfatal(owner->super.endpoints[EP_SERVER], TRUE); if (owner->policy_enable_longline) z_stream_line_set_split(owner->super.endpoints[EP_SERVER], TRUE); success = z_transfer2_simple_run(&t->super.super); z_stream_line_set_split(owner->super.endpoints[EP_SERVER], FALSE); z_stream_line_set_nul_nonfatal(owner->super.endpoints[EP_SERVER], FALSE); if (t->super.dst_write_state == DOT_DW_PREAMBLE) { /* nothing was written to destination */ switch (z_transfer2_get_stack_decision(&t->super.super)) { case ZV_REJECT: /*LOG This message indicates that the stacked proxy rejected the content and Zorp rejects the response. */ z_proxy_log(owner, POP3_ERROR, 2, "Stacked proxy rejected contents; info='%s'", z_transfer2_get_stack_info(&t->super.super)); g_snprintf(buf, sizeof(buf), "Content rejected (%s)", z_transfer2_get_stack_info(&t->super.super)); if (owner->reject_by_mail) pop3_error_msg(owner, buf); else pop3_response_reject(owner, buf); break; case ZV_ERROR: g_snprintf(buf, sizeof(buf), "Error occurred while transferring data (%s)", z_transfer2_get_stack_info(&t->super.super)); pop3_response_reject(owner, buf); owner->pop3_state = POP3_STATE_QUIT; break; default: pop3_response_write(owner); pop3_write_client(owner, ".\r\n"); break; } } else { pop3_write_client(owner, ".\r\n"); } if (owner->from) { g_string_free(owner->from, TRUE); owner->from = NULL; } if (owner->to) { g_string_free(owner->to, TRUE); owner->to = NULL; } if (owner->subject) { g_string_free(owner->subject, TRUE); owner->subject = NULL; } z_object_unref(&t->super.super.super); z_proxy_return(owner, success); } zorp-3.9.5/modules/pop3/pop3data.h000066400000000000000000000024601172670260400167460ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_POP3DATA_H_INCLUDED #define ZORP_MODULES_POP3DATA_H_INCLUDED #include "pop3.h" gboolean pop3_data_transfer(Pop3Proxy *owner); #endif zorp-3.9.5/modules/pop3/pop3misc.c000066400000000000000000000033601172670260400167630ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "pop3misc.h" #include void pop3_get_timestamp(Pop3Proxy *self) { gchar *left; gchar *right; z_proxy_enter(self); g_string_assign(self->timestamp, ""); if ((left = strchr(self->response_param->str, '<')) == NULL) z_proxy_return(self); if ((right = strchr(self->response_param->str, '>')) == NULL) z_proxy_return(self); if (left >= right) z_proxy_return(self); g_string_append_len(self->timestamp, left, right - left + 1); z_proxy_return(self); } zorp-3.9.5/modules/pop3/pop3misc.h000066400000000000000000000024531172670260400167720ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_POP3MISC_H_INCLUDED #define ZORP_MODULES_POP3MISC_H_INCLUDED #include "pop3.h" void pop3_get_timestamp(Pop3Proxy *self); #endif zorp-3.9.5/modules/pop3/pop3policy.c000066400000000000000000000341401172670260400173270ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "pop3policy.h" #define POP3_POLICY "pop3.policy" #define POP3_DEBUG "pop3.debug" #define POP3_ERROR "pop3.error" guint pop3_policy_command_hash_search(Pop3Proxy *self, gchar *command) { void *tmp = g_hash_table_lookup(self->commands_policy, command); return tmp != NULL; } gboolean pop3_hash_get_type(ZPolicyObj *tuple, guint *filter_type) { ZPolicyObj *tmp; if (!z_policy_seq_check(tuple)) { if (z_policy_var_parse(tuple, "i", filter_type)) return TRUE; /* not a sequence */ return FALSE; } tmp = z_policy_seq_getitem(tuple, 0); if (!z_policy_var_parse(tmp, "i", filter_type)) { /* policy syntax error */ z_policy_var_unref(tmp); return FALSE; } z_policy_var_unref(tmp); return TRUE; } guint pop3_policy_command_hash_do(Pop3Proxy *self) { guint rc; ZPolicyObj *res; ZPolicyObj *tmp = g_hash_table_lookup(self->commands_policy, self->command->str); ZPolicyObj *command_where = NULL; ZPolicyObj *answer_where = NULL; unsigned int command_do; z_proxy_enter(self); if (!tmp) { z_proxy_log(self, POP3_DEBUG, 6, "Policy does not contain this request, trying the default; request='%s'", self->command->str); tmp = g_hash_table_lookup(self->commands_policy, "*"); } if (!tmp) { /*LOG This message indicates that the policy does not contain any setting for the given request and Zorp rejects the request. Check the 'request' attribute. */ z_proxy_log(self, POP3_DEBUG, 5, "Policy does not contain this request, using hard-coded default; request='%s'", self->command->str); z_proxy_return(self, POP3_REQ_REJECT); } z_policy_lock(self->super.thread); if (!pop3_hash_get_type(tmp, &command_do)) { /*LOG This message indicates that the policy type is invalid for the given request and Zorp aborts the connection. Check the 'request' attribute. */ z_proxy_log(self, POP3_POLICY, 1, "Policy type is invalid; req='%s'", self->command->str); z_policy_unlock(self->super.thread); z_proxy_return(self, POP3_REQ_ABORT); } z_policy_unlock(self->super.thread); switch(command_do) { case POP3_REQ_ACCEPT_MLINE: self->response_multiline = TRUE; /* No Break */ case POP3_REQ_REJECT: case POP3_REQ_ACCEPT: rc = command_do; break; case POP3_REQ_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp, "(iOO)", &command_do, &command_where, &answer_where) && !z_policy_var_parse(tmp, "(iO)", &command_do, &command_where)) { /*LOG This message indicates that the policy for the given request is invalid and Zorp aborts the connection. Check the 'request' attribute. It is likely that the parameter for the POP3_REQ_POLICY is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse policy line; req='%s'",self->command->str); rc = POP3_REQ_ABORT; } else { res = z_policy_call_object(command_where, z_policy_var_build("(s)", self->command), self->super.session_id); if (res == NULL) { /*LOG This message indicates that the callback for the given request policy is invalid and Zorp aborts the connection. Check the 'request' attribute. It is likely that the parameter for the POP3_REQ_POLICY is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Error in policy call; req='%s'", self->command->str); rc = POP3_REQ_ABORT; } else { if (!z_policy_var_parse(res, "i", &rc)) { /*LOG This message indicates that the returned value of the callback for the given request policy is invalid and Zorp aborts the connection. Check the callback function. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse the return code; req='%s'", self->command->str); rc = POP3_REQ_ABORT; } else { switch(rc) { case POP3_REQ_ACCEPT_MLINE: self->response_multiline = TRUE; /* No Break */ case POP3_REQ_ACCEPT: rc = POP3_REQ_ACCEPT; break; case ZV_UNSPEC: case ZV_DROP: case POP3_REQ_REJECT: rc = POP3_REQ_REJECT; break; case POP3_REQ_ABORT: default: rc = POP3_REQ_ABORT; break; } } } } z_policy_unlock(self->super.thread); break; case POP3_REQ_ABORT: default: rc = POP3_REQ_ABORT; break; } z_proxy_return(self, rc); } guint pop3_policy_response_hash_do(Pop3Proxy *self) { guint rc; ZPolicyObj *res; ZPolicyObj *tmp; ZPolicyObj *command_where = NULL; ZPolicyObj *answer_where = NULL; unsigned int command_do; z_proxy_enter(self); if (self->command->len) tmp = g_hash_table_lookup(self->commands_policy, self->command->str); else tmp = g_hash_table_lookup(self->commands_policy, "GREETING"); if (!tmp) { z_proxy_log(self, POP3_DEBUG, 6, "Policy does not contain this request, trying the default; request='%s'", self->command->str); tmp = g_hash_table_lookup(self->commands_policy, "*"); } if (!tmp) { /*LOG This message indicates that the policy does not contain any setting for the given response and Zorp rejects the response. Check the 'request' attribute. */ z_proxy_log(self, POP3_DEBUG, 5, "Policy does not contain this request, using hard-coded default; request='%s'", self->command->str); z_proxy_return(self, POP3_RSP_REJECT); } z_policy_lock(self->super.thread); if (!pop3_hash_get_type(tmp, &command_do)) { /*LOG This message indicates that the policy type is invalid for the given response and Zorp aborts the connection. Check the 'request' attribute. */ z_proxy_log(self, POP3_POLICY, 1, "Policy type is invalid; req='%s'", self->command->str); z_policy_unlock(self->super.thread); z_proxy_return(self, POP3_RSP_ABORT); } z_policy_unlock(self->super.thread); switch(command_do) { case POP3_REQ_ACCEPT_MLINE: case POP3_REQ_ACCEPT: rc = POP3_RSP_ACCEPT; break; case POP3_REQ_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp, "(iOO)", &command_do, &command_where, &answer_where) && !z_policy_var_parse(tmp, "(iO)", &command_do, &command_where)) { /*LOG This message indicates that the policy for the given request/response is invalid and Zorp aborts the connection. Check the 'request' attribute. It is likely that the parameter for the POP3_REQ_POLICY is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse policy line; req='%s'", self->command->str); rc = POP3_RSP_ABORT; } else { if (answer_where) { res = z_policy_call_object(answer_where, z_policy_var_build("(s)", self->response_param), self->super.session_id); if (res == NULL) { /*LOG This message indicates that the callback for the given request policy is invalid and Zorp aborts the connection. Check the 'request' attribute. It is likely that the parameter for the POP3_REQ_POLICY is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Error in policy call; req='%s'", self->command->str); rc = POP3_RSP_ABORT; } else { if (!z_policy_var_parse(res, "i", &rc)) { /*LOG This message indicates that the returned value of the callback for the given response policy is invalid and Zorp aborts the connection. Check the callback function. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse return code; req='%s'", self->command->str); rc = POP3_RSP_ABORT; } else { switch(rc) { case POP3_RSP_ACCEPT: break; case ZV_UNSPEC: case POP3_RSP_REJECT: rc = POP3_RSP_REJECT; break; case POP3_RSP_ABORT: default: rc = POP3_RSP_ABORT; break; } } } } else { rc = POP3_RSP_ACCEPT; } } z_policy_unlock(self->super.thread); break; case POP3_REQ_REJECT: case POP3_REQ_ABORT: default: rc = POP3_RSP_ABORT; break; } z_proxy_return(self, rc); } gboolean pop3_policy_stack_hash_do(Pop3Proxy *self, ZStackedProxy **stacked) { guint rc; ZPolicyObj *res = NULL; ZPolicyObj *tmp = g_hash_table_lookup(self->command_stack, self->command->str); ZPolicyObj *command_where = NULL; ZPolicyObj *stack_proxy = NULL; unsigned int command_do; gboolean success = TRUE; z_proxy_enter(self); if (!tmp) tmp = g_hash_table_lookup(self->command_stack, "*"); if (!tmp) z_proxy_return(self, TRUE); z_policy_lock(self->super.thread); if (!pop3_hash_get_type(tmp, &command_do)) { /*LOG This message indicates that the stack policy type is invalid for the given response, so nothing will be stacked. Check the 'response_stack' attribute. */ z_proxy_log(self, POP3_POLICY, 1, "Stack policy type is invalid; req='%s'", self->command->str); z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); } switch(command_do) { case POP3_STK_NONE: rc = command_do; break; case POP3_STK_DATA: case POP3_STK_MIME: if (!z_policy_var_parse(tmp, "(iO)", &rc, &stack_proxy)) { /*LOG This message indicates that the stack policy for the given response is invalid and Zorp stacks nothing. Check the 'response_stack' attribute. It is likely that the parameter for the POP3_STK_MIME or POP3_STK_DATA is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse stack policy line; req='%s'", self->command->str); success = FALSE; } break; case POP3_STK_POLICY: if (!z_policy_var_parse(tmp, "(iO)", &rc, &command_where)) { /*LOG This message indicates that the stack policy for the given response is invalid and Zorp stacks nothing. Check the 'response_stack' attribute. It is likely that the parameter for the POP3_STK_POLICY is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse stack policy line; req='%s'", self->command->str); success = FALSE; } else { res = z_policy_call_object(command_where, z_policy_var_build("(s)", self->command->str), self->super.session_id); if (res == NULL) { /*LOG This message indicates that the callback for the given request policy is invalid and Zorp stacks nothing. Check the 'request' attribute. It is likely that the parameter for the POP3_STK_POLICY is invalid. */ z_proxy_log(self, POP3_POLICY, 1, "Error in policy call; req='%s'", self->command->str); success = FALSE; } else { if (!z_policy_var_parse(res, "i", &rc) && !z_policy_var_parse(res, "(iO)", &rc, &stack_proxy)) { /*LOG This message indicates that the returned value of the callback for the given response policy is invalid and Zorp stacks nothing. Check the callback function. */ z_proxy_log(self, POP3_POLICY, 1, "Cannot parse return code; req='%s'", self->command->str); success = FALSE; } z_policy_var_unref(res); } } break; } if (success && rc != POP3_STK_NONE && stack_proxy) success = z_proxy_stack_object(&self->super, stack_proxy, stacked, NULL); z_policy_unlock(self->super.thread); z_proxy_return(self, success); } zorp-3.9.5/modules/pop3/pop3policy.h000066400000000000000000000032501172670260400173320ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #ifndef ZORP_MODULES_POP3POLICY_H_INCLUDED #define ZORP_MODULES_POP3POLICY_H_INCLUDED #include "pop3.h" guint pop3_policy_command_hash_search(Pop3Proxy *self,gchar *command); gboolean ftp_hash_get_type(ZPolicyObj *tuple, guint *filter_type); guint pop3_policy_command_hash_do(Pop3Proxy *self); guint pop3_policy_response_hash_do(Pop3Proxy *self); gboolean pop3_policy_stack_hash_do(Pop3Proxy *self, ZStackedProxy **stacked); #endif zorp-3.9.5/modules/smtp/000077500000000000000000000000001172670260400151625ustar00rootroot00000000000000zorp-3.9.5/modules/smtp/Makefile.am000066400000000000000000000004731172670260400172220ustar00rootroot00000000000000SUBDIRS = . tests pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Smtp.py pkglib_LTLIBRARIES = libsmtp.la libsmtp_la_SOURCES = smtp.c smtpcmd.c smtpdata.c smtpmsg.c smtppolicy.c smtp.h smtpmsg.h EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/smtp/Smtp.py000066400000000000000000001471171172670260400164720ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : SaSa ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Proxy for the Simple Mail Transport Protocol. Simple Mail Transport Protocol (SMTP) is a protocol for transferring electronic mail messages from Mail User Agents (MUAs) to Mail Transfer Agents (MTAs). It is also used for exchanging mails between MTAs.
The SMTP protocol The main goal of SMTP is to reliably transfer mail objects from the client to the server. A mail transaction involves exchanging the sender and recipient information and the mail body itself.
Protocol elements SMTP is a traditional command based Internet protocol; the client issues command verbs with one or more arguments, and the server responds with a 3 digit status code and additional information. The response can span one or multiple lines, the continuation is indicated by an '-' character between the status code and text. The communication itself is stateful, the client first specifies the sender via the "MAIL" command, then the recipients using multiple "RCPT" commands. Finally it sends the mail body using the "DATA" command. After a transaction finishes the client either closes the connection using the "QUIT" command, or starts a new transaction with another "MAIL" command. SMTP protocol sample 220 mail.example.com ESMTP Postfix (Debian/GNU) EHLO client.host.name 250-mail.example.com 250-PIPELINING 250-SIZE 50000000 250-VRFY 250-ETRN 250-XVERP 250 8BITMIME MAIL From: <sender@sender.com> 250 Sender ok RCPT To: <account@recipient.com> 250 Recipient ok RCPT To: <account2@recipient.com> 250 Recipient ok DATA 354 Send mail body From: sender@sender.com To: account@receiver.com Subject: sample mail This is a sample mail message. Lines beginning with ..are escaped, another '.' character is perpended which is removed when the mail is stored by the client. . 250 Ok: queued as BF47618170 QUIT 221 Farewell
Extensions Originally SMTP had a very limited set of commands (HELO, MAIL, RCPT, DATA, RSET, QUIT, NOOP) but as of RFC 1869, an extension mechanism was introduced. The initial HELO command was replaced by an EHLO command and the response to an EHLO command contains all the extensions the server supports. These extensions are identified by an IANA assigned name. Extensions are used for example to implement inband authentication (AUTH), explicit message size limitation (SIZE) and explicit queue run initiation (ETRN). Each extension might add new command verbs, but might also add new arguments to various SMTP commands. The SMTP proxy has built in support for the most important SMTP extensions, further extensions can be added through customization.
Bulk transfer The mail object is transferred as a series of lines, terminated by the character sequence "CRLF '.' CRLF". When the '.' character occurs as the first character of a line, an escaping '.' character is prepended to the line which is automatically removed by the peer.
Proxy behavior The Smtp module implements the SMTP protocol as specified in RFC 2821. The proxy supports the basic SMTP protocol plus five extensions, namely: PIPELINING, SIZE, ETRN, 8BITMIME, and STARTTLS. All other ESMTP extensions are filtered by dropping the associated token from the EHLO response. If no connection can be established to the server, the request is rejected with an error message. In this case the proxy tries to connect the next mail exchange server.
Default policy for commands The abstract SMTP proxy rejects all commands and responses by default. Less restrictive proxies are available as derived classes (e.g.: SmtpProxy), or can be customized as required.
Configuring policies for SMTP commands and responses Changing the default behavior of commands can be done by using the hash attribute request. These hashes are indexed by the command name (e.g.: MAIL or DATA). Policies for responses can be configured using the response attribute, which is indexed by the command name and the response code. The possible actions are shown in the tables below. See for details. When looking up entries of the response attribute hash, the lookup precedence described in is used. SMTP extensions can be controlled using the extension hash, which is indexed by the extension name. The supported extensions (SMTP_EXT_PIPELINING; SMTP_EXT_SIZE; SMTP_EXT_ETRN; SMTP_EXT_8BITMIME) can be accepted or dropped (SMTP_EXT_ACCEPT or SMTP_EXT_DROP) individually or all at once using the SMTP_EXT_ALL index value.
Stacking The available stacking modes for this proxy module are listed in the following table. For additional information on stacking, see .
Related standards Simple Mail Transfer Protocol is described in RFC 2821. SMTP Service Extensions are described in the obsoleted RFC 1869. The STARTTLS extension is described in RFC 3207.
Attempt to identify the domain automatically. SMTP_GETDOMAIN_MAILNAME Read from /etc/mailname SMTP_GETDOMAIN_FQDN Get firewall's FQDN Action codes for SMTP requests SMTP_REQ_ACCEPT SMTP_REQ_REJECT SMTP_REQ_ABORT SMTP_REQ_POLICY Action codes for SMTP responses SMTP_RSP_ACCEPT SMTP_RSP_REJECT SMTP_RSP_ABORT SMTP_RSP_POLICY Action codes for SMTP extensions SMTP_EXT_ACCEPT Accept SMTP_EXT_DROP Drop Smtp stack hashes SMTP_STK_NONE SMTP_STK_MIME Logging levels of SMTP SMTP_POLICY 'smtp.policy' SMTP_DEBUG 'smtp.debug' SMTP_INFO smtp.info SMTP_ERROR smtp.error Action codes for SMTP requests Accept the request without any modification. Reject the request. The second parameter contains an SMTP status code, the third one an associated parameter which will be sent back to the client. Reject the request and terminate the connection. Action codes for SMTP responses Accept the response without any modification. Reject the response. The second parameter contains an SMTP status code, the third one an associated parameter which will be sent back to the client. Reject the response and terminate the connection. Stacking options for SMTP No additional proxy is stacked into the SMTP proxy. The data part including header information of the traffic is passed to the specified stacked proxy.
""" from Zorp import * from Proxy import Proxy, proxyLog from Matcher import AbstractMatcher, getMatcher # for compatibility from Matcher import SmtpInvalidRecipientMatcher from string import split, find, lower, replace from time import strftime from socket import gethostbyaddr, getfqdn import types SMTP_POLICY = 'smtp.policy' SMTP_DEBUG = 'smtp.debug' SMTP_INFO = 'smtp.info' SMTP_ERROR = 'smtp.error' SMTP_GETDOMAIN_MAILNAME = 'mailname' SMTP_GETDOMAIN_FQDN = 'fqdn' SMTP_REQ_ACCEPT = 1 SMTP_REQ_REJECT = 3 SMTP_REQ_ABORT = 4 SMTP_REQ_POLICY = 6 SMTP_RSP_ACCEPT = 1 SMTP_RSP_REJECT = 3 SMTP_RSP_ABORT = 4 SMTP_RSP_POLICY = 6 SMTP_EXT_PIPELINING = 0x0001 SMTP_EXT_SIZE = 0x0002 SMTP_EXT_ETRN = 0x0004 SMTP_EXT_8BITMIME = 0x0008 SMTP_EXT_AUTH = 0x0010 SMTP_EXT_STARTTLS = 0x0020 SMTP_EXT_ALL = 0x000F SMTP_EXT_ACCEPT = 1 SMTP_EXT_DROP = 5 SMTP_STK_NONE = 0 SMTP_STK_MIME = 1 class AbstractSmtpProxy(Proxy): """ Class encapsulating the abstract SMTP proxy. This class implements an abstract SMTP proxy - it serves as a starting point for customized proxy classes, but is itself not directly usable. Service definitions should refer to a customized class derived from AbstractSmtpProxy, or one of the predefined proxy classes. The following requests are permitted: HELO; MAIL; RCPT; DATA; RSET; QUIT; NOOP; EHLO; AUTH; ETRN. The following extensions are permitted: PIPELINING; SIZE; ETRN; 8BITMIME; STARTTLS. timeout 600000 Timeout in milliseconds. If no packet arrives within this in interval, the connection is dropped. add_received_header FALSE Add a Received: header into the email messages transferred by the proxy. resolve_host FALSE Resolve the client host from the IP address and add it to the Received line. Only takes effect if add_received_header is TRUE. domain_name If you want to set a fix domain name into the added Receive line, set this. Only takes effect if add_received_header is TRUE. autodetect_domain_from If you want Zorp to autodetect the domain name of the firewall and write it to the Received line, then set this. This attribute either set the method how the Zorp detect the mailname. Only takes effect if add_received_header is TRUE. interval_transfer_noop 600000 The interval between two NOOP commands sent to the server while waiting for the results of stacked proxies. max_request_length 256 Maximum allowed line length of client requests. max_auth_request_length 256 Maximum allowed length of a request during SASL style authentication. max_response_length 512 Maximum allowed line length of a server response. unconnected_response_code 554 Error code sent to the client if connecting to the server fails. require_crlf TRUE Specifies whether the proxy should enforce valid CRLF line terminations. request_command n/a When a command is passed to the policy level, its value can be changed to this value. request_param n/a When a command is passed to the policy level, the value of its parameter can be changed to this value. response_value n/a When a response is passed to the policy level, its value can be changed to this value. (It has effect only when the return value is not SMTP_*_ACCEPT.) response_param n/a When a response is passed to the policy level, the value of its parameter can be changed to this value. (It has effect only when the return value is not SMTP_*_ACCEPT.) request Normative policy hash for SMTP requests indexed by the command name (e.g.: "USER", "UIDL", etc.). See also . response Normative policy hash for SMTP responses indexed by the command name and the response code. See also . extensions Normative policy hash for ESMTP extension policy, indexed by the extension verb (e.g. ETRN). It contains an action tuple with the SMTP_EXT_* values as possible actions. permit_unknown_command FALSE Enable unknown commands. permit_long_responses FALSE Permit overly long responses, as some MTAs include variable parts in responses which might get very long. If enabled, responses longer than max_response_length are segmented into separate messages. If disabled, such responses are rejected. permit_omission_of_angle_brackets FALSE Permit MAIL From and RCPT To parameters without the normally required angle brackets around them. They will be added when the message leaves the proxy anyway. request_stack Attribute containing the stacking policy for SMTP commands. See . append_domain Domain to append to email addresses which do not specify domain name. An address is rejected if it does not contain a domain and append_domain is empty. active_extensions n/a Active extension bitmask, contains bits defined by the constants 'SMTP_EXT_*' tls_passthrough FALSE Change to passthrough mode after a successful STARTTLS request. Zorp does not process or change the encrypted traffic in any way, it is transported intact between the client and server. """ name = "smtp" def __init__(self, session): """ Initialize a SmtpProxy instance. Create and set up a SmtpProxy instance. session SESSION session this instance belongs to """ self.request_stack = {} Proxy.__init__(self, session) def loadSMTP(self): """ """ # greeting self.response["Null", "220"] = SMTP_RSP_ACCEPT self.response["Null", "554"] = SMTP_RSP_ACCEPT # permitted for all commands self.response["*", "421"] = SMTP_RSP_ACCEPT self.response["*", "500"] = SMTP_RSP_ACCEPT self.response["*", "501"] = SMTP_RSP_ACCEPT self.request["HELO"] = SMTP_REQ_ACCEPT self.response["HELO", "250"] = SMTP_RSP_ACCEPT self.response["HELO", "504"] = SMTP_RSP_ACCEPT self.response["HELO", "550"] = SMTP_RSP_ACCEPT self.request["MAIL"] = SMTP_REQ_ACCEPT self.response["MAIL", "250"] = SMTP_RSP_ACCEPT self.response["MAIL", "450"] = SMTP_RSP_ACCEPT self.response["MAIL", "451"] = SMTP_RSP_ACCEPT self.response["MAIL", "452"] = SMTP_RSP_ACCEPT self.response["MAIL", "503"] = SMTP_RSP_ACCEPT self.response["MAIL", "530"] = SMTP_RSP_ACCEPT self.response["MAIL", "550"] = SMTP_RSP_ACCEPT self.response["MAIL", "552"] = SMTP_RSP_ACCEPT self.response["MAIL", "553"] = SMTP_RSP_ACCEPT self.request["RCPT"] = SMTP_REQ_ACCEPT self.response["RCPT", "250"] = SMTP_RSP_ACCEPT self.response["RCPT", "251"] = SMTP_RSP_ACCEPT self.response["RCPT", "450"] = SMTP_RSP_ACCEPT self.response["RCPT", "451"] = SMTP_RSP_ACCEPT self.response["RCPT", "452"] = SMTP_RSP_ACCEPT self.response["RCPT", "503"] = SMTP_RSP_ACCEPT self.response["RCPT", "530"] = SMTP_RSP_ACCEPT self.response["RCPT", "550"] = SMTP_RSP_ACCEPT self.response["RCPT", "551"] = SMTP_RSP_ACCEPT self.response["RCPT", "552"] = SMTP_RSP_ACCEPT self.response["RCPT", "553"] = SMTP_RSP_ACCEPT self.response["RCPT", "554"] = SMTP_RSP_ACCEPT self.request["DATA"] = SMTP_REQ_ACCEPT self.response["DATA", "354"] = SMTP_RSP_ACCEPT # after data transmission self.response["DATA", "250"] = SMTP_RSP_ACCEPT self.response["DATA", "530"] = SMTP_RSP_ACCEPT self.response["DATA", "451"] = SMTP_RSP_ACCEPT self.response["DATA", "452"] = SMTP_RSP_ACCEPT self.response["DATA", "503"] = SMTP_RSP_ACCEPT # not allowed by RFC, but seems to be a common choice to reject mail self.response["DATA", "550"] = SMTP_RSP_ACCEPT self.response["DATA", "552"] = SMTP_RSP_ACCEPT self.response["DATA", "554"] = SMTP_RSP_ACCEPT self.request["RSET"] = SMTP_REQ_ACCEPT self.response["RSET", "250"] = SMTP_RSP_ACCEPT self.request["QUIT"] = SMTP_REQ_ACCEPT self.response["QUIT", "221"] = SMTP_RSP_ACCEPT self.request["NOOP"] = SMTP_REQ_ACCEPT self.response["NOOP", "250"] = SMTP_RSP_ACCEPT def loadESMTP(self): """ """ self.loadSMTP() self.extensions["PIPELINING"] = (SMTP_EXT_ACCEPT) self.extensions["SIZE"] = (SMTP_EXT_ACCEPT) self.extensions["ETRN"] = (SMTP_EXT_ACCEPT) self.extensions["AUTH"] = (SMTP_EXT_ACCEPT) self.extensions["8BITMIME"] = (SMTP_EXT_ACCEPT) self.extensions["STARTTLS"] = (SMTP_EXT_ACCEPT) self.request["EHLO"] = SMTP_REQ_ACCEPT self.response["EHLO", "250"] = SMTP_RSP_ACCEPT self.response["EHLO", "504"] = SMTP_RSP_ACCEPT self.response["EHLO", "550"] = SMTP_RSP_ACCEPT self.request["AUTH"] = SMTP_REQ_ACCEPT self.response["AUTH", "235"] = SMTP_RSP_ACCEPT self.response["AUTH", "334"] = SMTP_RSP_ACCEPT self.response["AUTH", "432"] = SMTP_RSP_ACCEPT self.response["AUTH", "454"] = SMTP_RSP_ACCEPT self.response["AUTH", "501"] = SMTP_RSP_ACCEPT self.response["AUTH", "503"] = SMTP_RSP_ACCEPT self.response["AUTH", "504"] = SMTP_RSP_ACCEPT self.response["AUTH", "534"] = SMTP_RSP_ACCEPT self.response["AUTH", "535"] = SMTP_RSP_ACCEPT self.response["AUTH", "538"] = SMTP_RSP_ACCEPT self.request["ETRN"] = SMTP_REQ_ACCEPT self.response["ETRN", "250"] = SMTP_RSP_ACCEPT self.response["ETRN", "251"] = SMTP_RSP_ACCEPT self.response["ETRN", "252"] = SMTP_RSP_ACCEPT self.response["ETRN", "253"] = SMTP_RSP_ACCEPT self.response["ETRN", "458"] = SMTP_RSP_ACCEPT self.response["ETRN", "459"] = SMTP_RSP_ACCEPT self.response["ETRN", "500"] = SMTP_RSP_ACCEPT self.response["ETRN", "501"] = SMTP_RSP_ACCEPT self.response["ETRN", "530"] = SMTP_RSP_ACCEPT self.request["STARTTLS"] = SMTP_REQ_ACCEPT self.response["STARTTLS", "220"] = SMTP_RSP_ACCEPT self.response["STARTTLS", "454"] = SMTP_RSP_ACCEPT self.response["STARTTLS", "501"] = SMTP_RSP_ACCEPT def generateReceived(self): """ """ if hasattr(self, "resolve_host") and self.resolve_host: try: from_hostaddr = gethostbyaddr(self.session.client_address.ip_s) from_domain = "%s [%s]" % (from_hostaddr[0], self.session.client_address.ip_s) except: from_domain = "[%s]" % (self.session.client_address.ip_s,) else: from_domain = "[%s]" % (self.session.client_address.ip_s,) if hasattr(self, "domain_name"): my_domain = self.domain_name elif hasattr(self, "autodetect_domain_from") and self.autodetect_domain_from == SMTP_GETDOMAIN_MAILNAME: try: mailname_handler = open("/etc/mailname") my_domain = mailname_handler.readline().strip() except IOError, s: log(None, SMTP_ERROR, 3, "Error reading mailname; error='%s'", (s,)) my_domain = "unknown" elif hasattr(self, "autodetect_domain_from") and self.autodetect_domain_from == SMTP_GETDOMAIN_FQDN: try: my_domain = getfqdn() except: my_domain = "unknown" else: my_domain = "unknown" cur_date = strftime("%a, %d %b %Y %H:%M:%S %z (%Z)") received_id = replace(self.session.owner.session_id, ":", ".") line = "Received: from %s (%s) by %s with %s id %s; %s\r\n" % (self.helo_string, from_domain, my_domain, self.protocol, received_id, cur_date) return line def requestStack(self): """ """ try: stack_proxy = self.request_stack["DATA"] except KeyError: try: stack_proxy = self.request_stack["*"] except: return None if (type(stack_proxy) == type(()) and stack_proxy[0] != SMTP_STK_NONE): return stack_proxy[1] return None class SmtpProxy(AbstractSmtpProxy): """ Default SMTP proxy based on AbstractSmtpProxy. SmtpProxy implements a basic SMTP Proxy based on AbstractSmtpProxy, with relay checking and sender/recipient check restrictions. (Exclamation marks and percent signs are not allowed in the e-mail addresses.) relay_zones Zorp zones that are relayed. The administrative hierarchy of the zone is also used. relay_check TRUE Enable/disable relay checking. relay_domains Domains mails are accepted for. Use Postfix style lists. (E.g.: '.example.com' allows every subdomain of example.com, but not example.com. To match example.com use 'example.com'.) relay_domains_matcher Domains mails are accepted for based on a matcher (e.g.: RegexpFileMatcher). sender_matcher Matcher class (e.g.: SmtpInvalidRecipientMatcher) used to check and filter sender e-mail addresses. recipient_matcher Matcher class (e.g.: SmtpInvalidRecipientMatcher) used to check and filter recipient e-mail addresses. permit_percent_hack FALSE Allow the '%' sign in the local part of e-mail addresses. permit_exclamation_mark FALSE Allow the '!' sign in the local part of e-mail addresses. error_soft FALSE BOOLEAN:FALSE:RW:RW Return a soft error condition when recipient filter does not match. If enabled, the proxy will try to re-validate the recipient and send the mail again. This option is useful when the server used for the recipient matching is down. """ sender_matcher = None recipient_matcher = None relay_domains_matcher = None def __init__(self, session): """ """ self.relay_domains = () self.relay_zones = () self.permit_percent_hack = FALSE self.permit_exclamation_mark = FALSE self.error_soft = FALSE AbstractSmtpProxy.__init__(self, session) def config(self): """ Default Smtp config Fill request hash with common ESMTP commands and allow all known SMTP extensions. """ self.loadESMTP() self.request["MAIL"] = (SMTP_REQ_POLICY, self.checkSender) self.request["RCPT"] = (SMTP_REQ_POLICY, self.checkRecipient) self.relay_check = TRUE def __post_config__(self): """ """ AbstractSmtpProxy.__post_config__(self) self.relay_zones_orig = {} if type(self.relay_zones) == types.TupleType or type(self.relay_zones) == types.ListType: for zone in self.relay_zones: self.relay_zones_orig[zone] = 1 else: if type(self.relay_zones) == types.StringType: self.relay_zones_orig[self.relay_zones] = 1 if type(self.relay_domains) == types.StringType: self.relay_domains = (self.relay_domains,) if self.sender_matcher: self.sender_matcher = getMatcher(self.sender_matcher) if self.recipient_matcher: self.recipient_matcher = getMatcher(self.recipient_matcher) if self.relay_domains_matcher: self.relay_domains_matcher = getMatcher(self.relay_domains_matcher) def checkSender(self, cmd, param): """ """ email = self.sanitizeAddress(param[5:]) try: if self.sender_matcher and self.sender_matcher.checkMatch(email): ## LOG ## # This message indicates that the sender address was administratively prohibited # and Zorp rejects the request. Check the 'sender_matcher' attribute. ## proxyLog(self, SMTP_POLICY, 3, "Sender address administratively prohibited; email='%s'" % email) if not self.error_soft: self.error_code = "550" self.error_info = "Sender address refused." else: self.error_code = "450" self.error_info = "Cannot verify sender at this time, come back later." return SMTP_REQ_REJECT except MatcherException: self.error_code = "450" self.error_info = "Cannot verify sender at this time, come back later" return SMTP_REQ_REJECT ## LOG ## # This message reports that the sender address check was successful and # Zorp accepts the request. ## proxyLog(self, SMTP_DEBUG, 6, "Sender check successful; email='%s'" % email) return SMTP_REQ_ACCEPT def checkRecipient(self, cmd, param): """ """ email = self.sanitizeAddress(param[3:]) try: (local, domain) = split(email, '@', 2) except ValueError: local = email domain = '' if not self.permit_percent_hack and local.find('%') != -1: ## LOG ## # This message indicates that the email address local-part contains a percent sign and # it is not permitted by the policy and Zorp rejects the request. Check the 'permit_percent_hack' # attribute. ## proxyLog(self, SMTP_POLICY, 3, "Forbidden percent found in address local-part; email='%s'" % email) self.error_code = '501' self.error_info = 'Malformed address' return SMTP_REQ_REJECT if not self.permit_exclamation_mark and local.find('!') != -1: ## LOG ## # This message indicates that the email address local-part contains a exclamation mark and # it is not permitted by the policy and Zorp rejects the request. Check the 'permit_exclamation_mark' # attribute. ## proxyLog(self, SMTP_POLICY, 3, "Forbidden exclamation mark found in address local-part; email='%s'" % email) self.error_code = '501' self.error_info = 'Malformed address' return SMTP_REQ_REJECT if self.relay_check and not self.relayCheck(email): ## LOG ## # This message indicates that relaying the given address is not permitted by the # policy and Zorp rejects the request. Check the 'relay_check' attribute. ## proxyLog(self, SMTP_POLICY, 3, "Relaying denied; email='%s'" % email) self.error_code = "554" self.error_info = "Relaying denied." return SMTP_REQ_REJECT else: ## LOG ## # This message reports that the relay check was successful and Zorp accepts the request. ## proxyLog(self, SMTP_DEBUG, 6, "Relay check successful; email='%s'" % email) try: if self.recipient_matcher and self.recipient_matcher.checkMatch(email): ## LOG ## # This message indicates that the given recipient address is administratively prohibited # and Zorp rejects the request. Check the 'recipient_matcher' attribute. ## proxyLog(self, SMTP_POLICY, 3, "Recipient address administratively prohibited; email='%s'" % email) if not self.error_soft: self.error_code = "554" self.error_info = "Recipient address refused." else: self.error_code = "450" self.error_info = "Cannot verify recipient at this time, come back later." return SMTP_REQ_REJECT except MatcherException: self.error_code = "450" self.error_info = "Cannot verify recipient at this time, come back later." return SMTP_REQ_REJECT ## LOG ## # This message reports that the recipient check was successful and Zorp accepts the request. ## proxyLog(self, SMTP_DEBUG, 6, "Recipient check successful; email='%s'" % email) return SMTP_REQ_ACCEPT def relayCheck(self, email): """ """ ## LOG ## # This message reports that Zorp checks the zone of the client. ## proxyLog(self, SMTP_DEBUG, 7, "Relay check, checking client_zone; client_zone='%s'" % self.session.client_zone.name) if self.relay_zones_orig.has_key("*"): return TRUE if self.zoneCheck(self.session.client_zone): return TRUE try: (local, domain) = split(email, '@', 2) except ValueError: local = email domain = '' ## LOG ## # This message reports that Zorp checks the domain name of the email. ## proxyLog(self, SMTP_DEBUG, 7, "Relay check, checking mail domain; local='%s', domain='%s'" % (local, domain)) for dom in self.relay_domains: f = find(lower(domain), lower(dom)) if dom[0] == '.': if (f != -1) and (f + len(dom) == len(domain)): return TRUE else: if (f == 0) and (len(dom) == len(domain)): return TRUE try: if self.relay_domains_matcher and self.relay_domains_matcher.checkMatch(domain): return TRUE except MatcherException: return FALSE return FALSE def zoneCheck(self, zone): """ """ if self.relay_zones_orig.has_key(zone.name): return TRUE else: if zone.admin_parent: return self.zoneCheck(zone.admin_parent) return FALSE zorp-3.9.5/modules/smtp/smtp.c000066400000000000000000001476631172670260400163320ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "smtp.h" #include #include #include #include #include #include #include #define SMTP_DEBUG "smtp.debug" #define SMTP_REQUEST "smtp.request" #define SMTP_REPLY "smtp.reply" /** * smtp_register_vars: * @self: SmtpProxy instance * * Registers python accessible variables. **/ static void smtp_register_vars(SmtpProxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "timeout", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->timeout); z_proxy_var_new(&self->super, "interval_transfer_noop", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->interval_transfer_noop); z_proxy_var_new(&self->super, "unconnected_response_code", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG, &self->unconnected_response_code); z_proxy_var_new(&self->super, "max_request_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_request_length); z_proxy_var_new(&self->super, "max_auth_request_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_auth_request_length); z_proxy_var_new(&self->super, "max_response_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_response_length); z_proxy_var_new(&self->super, "max_line_length", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->max_line_length); z_proxy_var_new(&self->super, "request_cmd", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->request); z_proxy_var_new(&self->super, "sender", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->sender); z_proxy_var_new(&self->super, "recipients", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->recipients); z_proxy_var_new(&self->super, "request_param", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->request_param); z_proxy_var_new(&self->super, "response_code", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->response); z_proxy_var_new(&self->super, "response_param", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->response_param); z_proxy_var_new(&self->super, "error_code", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->error_code); z_proxy_var_new(&self->super, "error_info", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->error_info); z_proxy_var_new(&self->super, "error_abort", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET, &self->error_abort); z_proxy_var_new(&self->super, "append_domain", Z_VAR_TYPE_STRING | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG | Z_VAR_GET | Z_VAR_SET, self->append_domain); z_proxy_var_new(&self->super, "permit_unknown_command", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET, &self->permit_unknown_command); z_proxy_var_new(&self->super, "permit_long_responses", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG | Z_VAR_GET, &self->permit_long_responses); z_proxy_var_new(&self->super, "permit_omission_of_angle_brackets", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG | Z_VAR_GET, &self->permit_omission_of_angle_brackets); z_proxy_var_new(&self->super, "require_crlf", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG | Z_VAR_GET, &self->require_crlf); z_proxy_var_new(&self->super, "permit_extensions", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG | Z_VAR_SET | Z_VAR_GET, &self->permit_extensions); z_proxy_var_new(&self->super, "extensions", Z_VAR_TYPE_HASH | Z_VAR_GET_CONFIG | Z_VAR_GET, self->extensions); z_proxy_var_new(&self->super, "request", Z_VAR_TYPE_HASH | Z_VAR_GET_CONFIG | Z_VAR_GET, self->request_policy); z_proxy_var_new(&self->super, "response", Z_VAR_TYPE_DIMHASH | Z_VAR_GET_CONFIG | Z_VAR_GET, self->response_policy); z_proxy_var_new(&self->super, "active_extensions", Z_VAR_TYPE_INT | Z_VAR_GET, &self->active_extensions); z_proxy_var_new(&self->super, "add_received_header", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET, &self->add_received_header); z_proxy_var_new(&self->super, "helo_string", Z_VAR_TYPE_STRING | Z_VAR_GET_CONFIG | Z_VAR_GET, self->helo_string); z_proxy_var_new(&self->super, "protocol", Z_VAR_TYPE_STRING | Z_VAR_GET_CONFIG | Z_VAR_GET, self->protocol); z_proxy_var_new(&self->super, "tls_passthrough", Z_VAR_TYPE_INT | Z_VAR_SET_CONFIG | Z_VAR_GET, &self->tls_passthrough); z_proxy_var_new(&self->super, "sanitizeAddress", Z_VAR_TYPE_METHOD | Z_VAR_GET, self, smtp_policy_sanitize_address); z_proxy_return(self); } /** * smtp_set_defaults: * @self: SmtpProxy instance * * Set default values for various attributes in @self. **/ static void smtp_set_defaults(SmtpProxy *self) { z_proxy_enter(self); self->request = g_string_sized_new(8); self->request_param = g_string_sized_new(128); self->response = g_string_sized_new(4); self->response_param = g_string_sized_new(128); self->sender = g_string_sized_new(32); self->sanitized_recipient = g_string_sized_new(32); self->recipients = g_string_sized_new(32); self->error_code = g_string_sized_new(4); self->error_info = g_string_new("Invalid command"); self->append_domain = g_string_sized_new(0); self->auth_request = g_string_sized_new(16); self->helo_string = g_string_sized_new(32); self->protocol = g_string_sized_new(6); self->timeout = 600000; self->interval_transfer_noop = 60000; self->buffer_size = 4096; self->active_extensions = 0; self->max_request_length = 512; self->max_auth_request_length = 256; self->max_response_length = 512; self->max_line_length = 4096; self->require_crlf = TRUE; self->unconnected_response_code = 554; self->start_tls_ok[EP_CLIENT] = FALSE; self->start_tls_ok[EP_SERVER] = FALSE; self->tls_passthrough = FALSE; self->extensions = g_hash_table_new(g_str_hash, g_str_equal); self->request_policy = g_hash_table_new(g_str_hash, g_str_equal); self->response_policy = z_dim_hash_table_new(2, 2, DIMHASH_WILDCARD, DIMHASH_CONSUME); z_proxy_return(self); } /** * smtp_config_init: * @self: SmtpProxy instance * * Initialize a configuration after the config() method had been called. It * currently does nothing. **/ static gboolean smtp_config_init(SmtpProxy *self G_GNUC_UNUSED) { z_proxy_enter(self); z_proxy_return(self, TRUE); } /* request related functions */ /** * smtp_parse_request: * @self: SmtpProxy instance * @line: a complete line to parse as an SMTP request (non necessarily NULL terminated) * @line_len: length of @line * * This function parses an incoming line as an SMTP request and stores * various request parts in the @request and @request_param fields of @self. * * Returns: TRUE to indicate success **/ static gboolean smtp_parse_request(SmtpProxy *self, gchar *line, gint line_len) { gint i; z_proxy_enter(self); g_string_truncate(self->request, 0); i = 0; while (i < line_len) { if (line[i] == ' ') break; else if (isalpha(line[i])) g_string_append_c(self->request, toupper(line[i])); /* NOTE we convert command to uppercase here so policy lookup works */ i++; } if (i < line_len && line[i] != ' ') { /*LOG This message indicates that the request command verb is invalid and Zorp rejects the request. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid command verb in request; line='%.*s'", line_len, line); z_proxy_return(self, FALSE); } i++; if (line_len > i) g_string_assign_len(self->request_param, line + i, line_len - i); else g_string_assign(self->request_param, ""); /*LOG This message reports that the request was parsed successfully. */ z_proxy_log(self, SMTP_REQUEST, 7, "Request parsed; request='%s', param='%s'", self->request->str, self->request_param->str); z_proxy_return(self, TRUE); } /** * smtp_fetch_request: * @self: SmtpProxy instance * * This function reads and parses an SMTP request from the client. * * Returns: TRUE for success **/ static gboolean smtp_fetch_request(SmtpProxy *self) { GIOStatus res; gchar *line; gsize line_len; z_proxy_enter(self); /*LOG This message reports that the request is going to be fetched. */ z_proxy_log(self, SMTP_DEBUG, 6, "Fetching request;"); res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &line, &line_len, NULL); if (res != G_IO_STATUS_NORMAL) { if (res == G_IO_STATUS_ERROR || res == G_IO_STATUS_EOF) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; } else if (res != G_IO_STATUS_AGAIN) { self->error_abort = TRUE; } z_proxy_return(self, FALSE); } if (line_len > self->max_request_length) { /*LOG This message indicates that the request line is too long and Zorp rejects the request. Check the 'max_request_length' attribute. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Request line too long; length='%zd', max='%d'", line_len, self->max_request_length); z_proxy_return(self, FALSE); } if (!smtp_parse_request(self, line, line_len)) z_proxy_return(self, FALSE); self->request_cmd = g_hash_table_lookup(known_commands, self->request->str); z_proxy_return(self, TRUE); } /** * smtp_process_request: * @self: SmtpProxy instance * * Process a request read by @smtp_fetch_request. * * Returns: TRUE to indicate success. **/ static SmtpRequestTypes smtp_process_request(SmtpProxy *self) { SmtpRequestTypes res = SMTP_REQ_ACCEPT; z_proxy_enter(self); /*LOG This message reports that the request is going to be processed. */ z_proxy_log(self, SMTP_DEBUG, 6, "Processing request;"); if (self->request_cmd && !(self->request_cmd->smtp_state & self->smtp_state)) { res = SMTP_REQ_REJECT; g_string_assign(self->error_code, "503"); g_string_assign(self->error_info, "Invalid command in this state"); /*LOG This message indicates that the given command is not permitted in this state of the communication and Zorp rejects the request. */ z_proxy_log(self, SMTP_VIOLATION, 4, "Command not permitted in this state; request='%s', state='%d'", self->request->str, self->smtp_state); z_proxy_return(self, res); } if (self->request_cmd && self->request_cmd->command_parse) { res = self->request_cmd->command_parse(self); if (res != SMTP_REQ_ACCEPT) z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid SMTP command; request='%s', param='%s'", self->request->str, self->request_param->str); } if (res == SMTP_REQ_ACCEPT) { res = smtp_policy_check_request(self); if (res != SMTP_REQ_ACCEPT) z_proxy_log(self, SMTP_POLICY, 2, "Request not allowed by policy; request='%s', verdict='%d'", self->request->str, res); } if (!self->request_cmd && !self->permit_unknown_command && res != SMTP_REQ_ACCEPT) { /*LOG This message indicates that the given request is unknown and Zorp rejects it. Check the 'permit_unknown_command' and the 'request' attributes. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Unknown command; request='%s'", self->request->str); z_proxy_return(self, res); } if (res == SMTP_REQ_ABORT) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; } if (res == SMTP_REQ_ACCEPT && self->request_cmd && self->request_cmd->action_do) { res = self->request_cmd->action_do(self); if (res != SMTP_REQ_ACCEPT && res != SMTP_REQ_NOOP) z_proxy_log(self, SMTP_VIOLATION, 2, "Error processing SMTP request; request='%s', param='%s'", self->request->str, self->request_param->str); } z_proxy_return(self, res); } /** * smtp_copy_request: * @self: SmtpProxy instance * * Copy a request from the internal state to the stream representing the * server. * * Returns: TRUE to indicate success **/ static gboolean smtp_copy_request(SmtpProxy *self) { gchar newline[self->max_request_length + 3]; gint len; gsize bytes_written; GIOStatus res; z_proxy_enter(self); /*LOG This message reports that the request is going to be copied to the server. */ z_proxy_log(self, SMTP_DEBUG, 6, "Copying request to server; request='%s', param='%s'", self->request->str, self->request_param->str); if (self->request_param->len > 0) g_snprintf(newline, sizeof(newline), "%s %s\r\n", self->request->str, self->request_param->str); else g_snprintf(newline, sizeof(newline), "%s\r\n", self->request->str); len = strlen(newline); res = z_stream_write(self->super.endpoints[EP_SERVER], newline, len, &bytes_written, NULL); if (res != G_IO_STATUS_NORMAL) { /*LOG This message indicates that an error occurred during sending the request to the server. */ z_proxy_log(self, SMTP_ERROR, 3, "Error sending request; request='%s', param='%s'", self->request->str, self->request_param->str); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } /* authentication requests */ static gboolean smtp_fetch_auth_request(SmtpProxy *self) { GIOStatus res; gchar *line; gsize line_len; z_proxy_enter(self); /*LOG This message reports that the authentication request is going to be fetched. */ z_proxy_log(self, SMTP_DEBUG, 6, "Fetching authentication request;"); res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &line, &line_len, NULL); if (res != G_IO_STATUS_NORMAL) { if (res == G_IO_STATUS_ERROR) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; } z_proxy_return(self, FALSE); } if (line_len > self->max_auth_request_length) { /*LOG This message indicates that the authentication request line is too long and Zorp rejects the request. Check the 'max_auth_request_length' attribute. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Auth request line too long; length='%zd', max='%d'", line_len, self->max_auth_request_length); z_proxy_return(self, FALSE); } /* FIXME: verify whether the line contains valid mime64 encoding */ g_string_assign_len(self->auth_request, line, line_len); z_proxy_return(self, TRUE); } /** * smtp_copy_auth_request: * @self: SmtpProxy instance * * Copy an authentication request from the internal state to the stream * representing the server. * * Returns: TRUE to indicate success **/ static gboolean smtp_copy_auth_request(SmtpProxy *self) { gchar newline[self->max_auth_request_length + 3]; gint len; gsize bytes_written; GIOStatus res; z_proxy_enter(self); /*LOG This message reports that the authentication request is going to be copied to the server. */ z_proxy_log(self, SMTP_DEBUG, 6, "Copying authentication request to server;"); g_snprintf(newline, sizeof(newline), "%s\r\n", self->auth_request->str); len = strlen(newline); res = z_stream_write(self->super.endpoints[EP_SERVER], newline, len, &bytes_written, NULL); if (res != G_IO_STATUS_NORMAL) { /*LOG This message indicates that an error occurred during sending the authentication request to the server. */ z_proxy_log(self, SMTP_ERROR, 3, "Error sending authentication request;"); z_proxy_return(self, FALSE); } z_proxy_return(self, TRUE); } /* response handling */ /** * smtp_clear_response: * @self: SmtpProxy instance * * Clear the currently stored response and free associated storage. **/ void smtp_clear_response(SmtpProxy *self) { GList *p, *pnext; g_string_truncate(self->response, 0); g_string_truncate(self->response_param, 0); for (p = self->response_lines, pnext = NULL; p; p = pnext) { g_string_free((GString *) p->data, TRUE); pnext = p->next; g_list_free_1(p); } self->response_lines = NULL; } /** * smtp_set_response: * @self: SmtpProxy instance * @code: SMTP reply code * @param: SMTP reply parameter * * Set the internal proxy state to contain the specified SMTP reply. * Primarily used to set an error response to be sent back to the client. **/ static void smtp_set_response(SmtpProxy *self, gchar *code, gchar *param) { z_proxy_enter(self); smtp_clear_response(self); g_string_assign(self->response, code); g_string_assign(self->response_param, param); z_proxy_return(self); } /** * smtp_parse_response: * @self: SmtpProxy instance * @line: the whole line as sent by the server * @line_len: the length of @line * @continuation: whether the response is to be continued * @code: the address of the reply code is returned here * @code_len: the length of the @code is returned here * @text: the address of textual parameter of the reply is returned here * @text_len: the length of @text is returned here * * Splits an incoming response line into parts, and returns them in * appropriate parameters. Returns TRUE to indicate success. **/ static gboolean smtp_parse_response(SmtpProxy *self G_GNUC_UNUSED, gchar *line, gint line_len, gboolean *continuation, gchar **code, gint *code_len, gchar **text, gint *text_len) { gint i; z_proxy_enter(self); if (line_len < 3) { z_proxy_log(self, SMTP_VIOLATION, 2, "Too small response; line='%.*s'", line_len, line); z_proxy_return(self, FALSE); } for (i = 0; i < 3; i++) { if (!isdigit(line[i])) { /*LOG This message indicates that the response contains non-numeric characters and Zorp rejects the response. */ z_proxy_log(self, SMTP_VIOLATION, 2, "SMTP reply contains non-numeric characters; line='%.*s'", line_len, line); z_proxy_return(self, FALSE); } } *code = line; *code_len = 3; if (line_len > 3) { if (line[3] == '-') { /* continuation */ *continuation = TRUE; } else if (line[3] == ' ') { *continuation = FALSE; } else { /*LOG This message indicates that the continuation character of the response contains an invalid character and Zorp rejects the response. The response must contain ' ' or '-' after the response code. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid continuation character; line='%.*s'", line_len, line); z_proxy_return(self, FALSE); } *text_len = line_len - 4; *text = line + 4; } else { *text_len = 0; *text = NULL; } z_proxy_return(self, TRUE); } /** * smtp_fetch_response: * @self: SmtpProxy instance * * This function reads and parses an incoming SMTP request and stores the * results in @self. **/ static gboolean smtp_fetch_response(SmtpProxy *self) { GIOStatus res; gchar *line, *code, *text; gsize line_len; gint code_len, text_len; gboolean continuation = TRUE, first = TRUE; gboolean success = FALSE; z_proxy_enter(self); /*LOG This message reports that the response is going to be fetched. */ z_proxy_log(self, SMTP_DEBUG, 6, "Fetching response;"); smtp_clear_response(self); while (continuation) { res = z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &line_len, NULL); if (res != G_IO_STATUS_NORMAL) { if (res == G_IO_STATUS_ERROR) self->error_abort = TRUE; goto error_exit; } if ((guint) line_len > self->max_response_length) { if (!self->permit_long_responses) { /*LOG This message indicates that the response line is too long and Zorp rejects the response. Check the 'max_response_length' attribute. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Response line too long; line='%.*s', length='%" G_GSIZE_FORMAT "', max_response_length='%d'", (gint) line_len, line, line_len, self->max_response_length); goto error_exit; } else { line_len = self->max_response_length; z_proxy_log(self, SMTP_VIOLATION, 3, "Response line was too long, truncated; length='%" G_GSIZE_FORMAT "', max_response_length='%d'", line_len, self->max_response_length); } } if (!smtp_parse_response(self, line, line_len, &continuation, &code, &code_len, &text, &text_len)) goto error_exit; /* the error is logged by parse_response */ if (first) { g_string_assign_len(self->response, code, code_len); g_string_assign_len(self->response_param, text, text_len); first = FALSE; } else { if (strncmp(self->response->str, code, code_len) != 0) { /* hmm, return codes in continuation lines differs from the * first */ /*LOG This message indicates that the reply code has changed in the continuation lines and Zorp rejects the response. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid SMTP reply, reply code changed; response='%s', line='%.*s'", self->response->str, (gint) line_len, line); goto error_exit; } self->response_lines = g_list_prepend(self->response_lines, g_string_new_len(text, text_len)); } } success = TRUE; /*LOG This message reports that the response is parsed successfully. */ z_proxy_log(self, SMTP_RESPONSE, 7, "Response parsed; response='%s', param='%s'", self->response->str, self->response_param->str); error_exit: self->response_lines = g_list_reverse(self->response_lines); z_proxy_return(self, success); } /** * Check if the response is accepted according to the response type * @param res the result to check * * @return TRUE if the response was accepted * FALSE otherwise */ static inline gboolean smtp_response_accepted(SmtpResponseTypes res) { return (res == SMTP_RSP_ACCEPT || res == SMTP_RSP_NOOP); } /** * smtp_process_response: * @self: SmtpProxy instance * * Process a response by calling the appropriate command specific parsing * function. **/ static SmtpResponseTypes smtp_process_response(SmtpProxy *self) { SmtpResponseTypes res; /*LOG This message indicates that the response is going to be processed. */ z_proxy_log(self, SMTP_DEBUG, 6, "Processing response;"); res = smtp_policy_check_response(self); if (res == SMTP_RSP_ACCEPT) { if (self->request_cmd && self->request_cmd->response_parse) { res = self->request_cmd->response_parse(self); if (!smtp_response_accepted(res)) { z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid SMTP response; request='%s', response='%s'", self->request->str, self->response->str); } } } else { z_proxy_log(self, SMTP_POLICY, 2, "Response not allowed by policy; request='%s', response='%s'", self->request->str, self->response->str); } if (res == SMTP_RSP_ABORT) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; } return res; } /** * smtp_copy_response: * @self: SmtpProxy instance * * Copy the parsed response to the client by formatting the appropriate * protocol elements. **/ gboolean smtp_copy_response(SmtpProxy *self) { GList *p; GString *response; gsize bytes_written; gboolean success = TRUE; z_proxy_enter(self); /*LOG This message reports that the response is going to be copied to the client. */ z_proxy_log(self, SMTP_DEBUG, 6, "Copying response to client;"); response = g_string_sized_new(64); if (self->response_lines || self->response_param->len) g_string_sprintf(response, "%s%c%s\r\n", self->response->str, self->response_lines ? '-' : ' ', self->response_param->str); else g_string_sprintf(response, "%s\r\n", self->response->str); for (p = self->response_lines; p; p = p->next) g_string_sprintfa(response, "%s%c%s\r\n", self->response->str, p->next ? '-' : ' ', ((GString *) p->data)->str); if (z_stream_write(self->super.endpoints[EP_CLIENT], response->str, response->len, &bytes_written, NULL) != G_IO_STATUS_NORMAL) { /*LOG This message indicates that an error occurred during sending the response to the client. */ z_proxy_log(self, SMTP_ERROR, 3, "Error sending SMTP reply;"); success = FALSE; } g_string_free(response, TRUE); z_proxy_return(self, success); } /* general I/O entry points when the protocol is running */ /** * smtp_init_streams: * @self: SmtpProxy instance * * Initialize server and client side streams. * * Returns: TRUE to indicate success **/ static gboolean smtp_init_streams(SmtpProxy *self) { ZStream *tmpstream; z_proxy_enter(self); self->super.endpoints[EP_CLIENT]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_CLIENT]; self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_NUL_NONFATAL | ZRL_EOL_CRLF | (self->require_crlf ? ZRL_EOL_FATAL : 0)); z_stream_unref(tmpstream); /** * When server side SSL handshake fails the proxy is in * SMTP_PROXY_UNCONNECTED_GREET state, but there is an * endpoint to the server */ if (self->super.endpoints[EP_SERVER] && self->proxy_state != SMTP_PROXY_UNCONNECTED_GREET) { self->super.endpoints[EP_SERVER]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_SERVER]; self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_NUL_NONFATAL | ZRL_EOL_CRLF | (self->require_crlf ? ZRL_EOL_FATAL : 0) | (self->permit_long_responses ? ZRL_TRUNCATE : 0)); z_stream_unref(tmpstream); self->proxy_state = SMTP_PROXY_RESPONSE; } else { self->proxy_state = SMTP_PROXY_UNCONNECTED_GREET; } z_proxy_return(self, TRUE); } static gboolean smtp_generate_noop(SmtpProxy *self) { gboolean policy_rejected; g_string_assign(self->request, "NOOP"); g_string_assign(self->request_param, ""); if (!smtp_copy_request(self) || !smtp_fetch_response(self)) { return FALSE; } else { policy_rejected = !smtp_response_accepted(smtp_process_response(self)); if (strcmp(self->response->str, "250") == 0 && policy_rejected) { /*LOG This message indicates that the response code 250 for the NOOP request is required and Zorp ignores the invalid policy while generating NOOPs to the server. */ z_proxy_log(self, SMTP_POLICY, 3, "Invalid policy ignored, allowing 250 response to NOOP is required;"); } } return TRUE; } gboolean smtp_generate_received(SmtpProxy *self, GString **dst_string) { gchar *received_line; ZPolicyObj *res; gboolean ret = FALSE; gboolean called; z_policy_lock(self->super.thread); res = z_policy_call(self->super.handler, "generateReceived", z_policy_var_build("()"), &called, self->super.session_id); if (res) { if (!z_policy_var_parse(res, "s", &received_line)) { z_proxy_log(self, SMTP_ERROR, 3, "Couldn't generate received line; error='wrong return value'"); } else { *dst_string = g_string_new(received_line); ret = TRUE; } z_policy_var_unref(res); } else { z_proxy_log(self, SMTP_ERROR, 3, "Couldn't generate received line; error='exception occured'"); } z_policy_unlock(self->super.thread); return ret; } void smtp_reset_state(SmtpProxy *self) { self->smtp_state = SMTP_STATE_EHLO; g_string_truncate(self->sender, 0); g_string_truncate(self->recipients, 0); } /** * smtp_format_stack_info: * @self: SmtpProxy instance * @stack_info: Info sent by stacked proxy * * Set up a well-formed error string from the info * sent by stacked proxy. Check if the line has * character with have problem when sent as smtp response. * */ void smtp_format_stack_info(SmtpProxy *self, const gchar *msg, const gchar *stack_info) { const guchar *search; for (search = (const guchar *) stack_info; *search < 127 && !g_ascii_iscntrl(*search) && *search != 0; search++) ; g_string_printf(self->error_info, "%s (%.*s)", msg, (gint) ((gchar *)search - stack_info), stack_info); return; } static gboolean smtp_process_transfer(SmtpProxy *self) { ZTransfer2Result tr; gboolean policy_rejected; g_string_assign(self->error_code, "550"); g_string_assign(self->error_info, "Error storing message"); tr = ZT2_RESULT_FAILED; if (smtp_transfer_is_data_delayed(self->transfer)) { gint suspend_reason = SMTP_TRANSFER_SUSPEND_DATA; /* make sure the server's timeout is extended (maybe the client waited * a lot before sending DATA) */ if (!smtp_generate_noop(self)) { goto error_reject_data; } /* we are entered here prior sending the DATA command */ /* we respond in the name of the server to let our child proxy * have a chance to reject the contents (otherwise an empty * message would be sent) */ g_string_assign(self->response, "354"); g_string_assign(self->response_param, "Go on, send your message"); if (!smtp_copy_response(self)) { /* the client probably closed its connection, we should attempt to * write a 421 and close the session */ goto error_before_transfer; } /* from this point the client sent a "DATA" and we responded to it * with 354, Thus the client is sending the mail body */ do { tr = z_transfer2_run(self->transfer); if (tr == ZT2_RESULT_SUSPENDED) { suspend_reason = z_transfer2_get_suspend_reason(self->transfer); if (suspend_reason == SMTP_TRANSFER_SUSPEND_NOOP) { if (!smtp_generate_noop(self)) { goto error_in_transfer; } } } } while (tr == ZT2_RESULT_SUSPENDED && suspend_reason != SMTP_TRANSFER_SUSPEND_DATA); /* still receiving the mail body */ if (tr == ZT2_RESULT_SUSPENDED) { g_string_assign(self->request, "DATA"); g_string_assign(self->request_param, ""); if (!smtp_copy_request(self)) { /* the server probably closed its connection */ /* we need to fetch the end of the mail body, and return 550 to indicate failure and send RSET to server */ goto error_reset; } if (!smtp_fetch_response(self)) { /* we need to fetch the end of the mail body, and return 550 to indicate failure */ goto error_reset; } policy_rejected = !smtp_response_accepted(smtp_process_response(self)); if (strcmp(self->response->str, "354") != 0) { /* we need to fetch the end of the mail body, and return 550 to indicate failure */ goto error_reset; } else if (policy_rejected) { /*LOG This message indicates that the response code 354 for the DATA request is required and Zorp ignores the invalid policy during data transfer to the server. */ z_proxy_log(self, SMTP_POLICY, 3, "Invalid policy ignored, allowing 354 response to DATA is required;"); } do { tr = z_transfer2_run(self->transfer); } while (tr == ZT2_RESULT_SUSPENDED); /* ok, the transfer either succeeded or it failed but it is ended. * if it was a failure we return 550 and go on fetching the next * request, if it was a success we go on fetching the next * response from the server */ } else if (tr == ZT2_RESULT_FINISHED) { /* empty message */ if (z_transfer2_get_stack_decision(self->transfer) == ZV_REJECT) { /*LOG This message indicates that the content was declared invalid by the stacked proxy and Zorp rejects it. */ z_proxy_log(self, SMTP_POLICY, 3, "Invalid contents; stack_info='%s'", z_transfer2_get_stack_info(self->transfer)); smtp_format_stack_info(self, "Error storing message", z_transfer2_get_stack_info(self->transfer)); } else if (z_transfer2_get_stack_decision(self->transfer) == ZV_DROP) { z_proxy_log(self, SMTP_POLICY, 3, "Message dropped, invalid contents; stack_info='%s'", z_transfer2_get_stack_info(self->transfer)); g_string_assign(self->error_code, "250"); smtp_format_stack_info(self, "Message discarded", z_transfer2_get_stack_info(self->transfer)); } else if (z_transfer2_get_stack_decision(self->transfer) == ZV_ERROR) { /*LOG This message inidicates that an error occured during stacked proxy handle the contents and Zorp send back a temporary error message. */ z_proxy_log(self, SMTP_POLICY, 3, "Error occured while scanning contents; stack_info='%s'", z_transfer2_get_stack_info(self->transfer)); g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); } else { /*LOG This message indicates that an empty message is received from the stacked proxy and Zorp rejects it. */ z_proxy_log(self, SMTP_ERROR, 3, "Rejecting empty message;"); } goto error_reset; } } else { /* the DATA command was sent and its response is received & * copied back to the client, check if we really need to * transfer data */ if (strcmp(self->response->str, "354") == 0) { /* ok, our DATA command was accepted, go on sending the data stream */ tr = z_transfer2_run(self->transfer); while (tr == ZT2_RESULT_SUSPENDED) tr = z_transfer2_run(self->transfer); } } if (tr == ZT2_RESULT_FINISHED) self->proxy_state = SMTP_PROXY_RESPONSE; else if (tr == ZT2_RESULT_ABORTED) goto error_abort; else if (tr == ZT2_RESULT_FAILED) goto error_in_transfer; return TRUE; error_reject_data: g_string_assign(self->response, "450"); g_string_assign(self->response_param, "Mailbox unavailable, try again"); z_proxy_log(self, SMTP_ERROR, 2, "Server closed the connection before transmission;"); return FALSE; error_before_transfer: self->error_abort = TRUE; g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); z_proxy_log(self, SMTP_ERROR, 2, "Client closed the connection before transmission;"); return FALSE; error_abort: self->error_abort = TRUE; g_string_assign(self->error_code, "550"); g_string_assign(self->error_info, "Mail body error (probably incorrect CRLF sequence)"); z_proxy_log(self, SMTP_VIOLATION, 2, "Transaction aborted, some data may have been sent;"); return FALSE; error_reset: g_string_assign(self->request, "RSET"); g_string_assign(self->request_param, ""); if (!smtp_copy_request(self) || !smtp_fetch_response(self) || !smtp_response_accepted(smtp_process_response(self))) { /*LOG This message indicates that Zorp was unable to send RSET command to the server. */ z_proxy_log(self, SMTP_ERROR, 3, "Error sending RSET to the server;"); } error_in_transfer: /* fetch the remaining mail body and return 550 */ z_transfer2_rollback(self->transfer); z_proxy_log(self, SMTP_ERROR, 2, "Transaction failed, some data may have been sent;"); return FALSE; } /** * smtp_config: * @s: SmtpProxy instance * * The config method for the SmtpProxy class. **/ static gboolean smtp_config(ZProxy *s) { SmtpProxy *self = (SmtpProxy *) s; gboolean success = FALSE; z_proxy_enter(self); self->poll = z_poll_new(); smtp_set_defaults(self); smtp_register_vars(self); if (Z_SUPER(s, ZProxy)->config(s)) success = smtp_config_init(self); z_proxy_return(self, success); } /** * Finish callback for our plug session * @param session the session object (unused) * @param user_data proxy owning the session * * Called by the plugsession callbacks when the channels have been * closed. We simply transition to SMTP_STATE_QUIT to exit the main * loop. */ static void smtp_plug_finish(ZPlugSession *session G_GNUC_UNUSED, gpointer user_data) { SmtpProxy *self = (SmtpProxy *) user_data; self->smtp_state = SMTP_STATE_QUIT; } /** * Timeout callback for our plug session * @param session the session object (unused) * @param user_data proxy owning the session * * Called by the plugsession callbacks when the channel timed out. We * go to SMTP_STATE_QUIT so that we exit our main loop in the next * iteration. */ static void smtp_plug_timeout(ZPlugSession *session G_GNUC_UNUSED, gpointer user_data) { SmtpProxy *self = (SmtpProxy *) user_data; self->smtp_state = SMTP_STATE_QUIT; } /** * Start a plug session with the streams of our proxy * @param self our SmtpProxy instance * * Fall back to plug mode -- don't actually process anything, just get * data from client to server and back. We set streams to * non-blocking, start a plug session and then iterate until the * session exits. * * On return, the streams of the proxy are in an inconsistent state * (might have been switched to non-blocking, for example). * @return TRUE on success * FALSE if there was an error during the transfer */ static gboolean smtp_plug_do_transfer(SmtpProxy *self) { z_proxy_enter(self); /* set up plug session parameters */ self->start_tls_fallback_data.copy_to_server = TRUE; self->start_tls_fallback_data.copy_to_client = TRUE; self->start_tls_fallback_data.timeout = self->timeout; self->start_tls_fallback_data.buffer_size = 2048; self->start_tls_fallback_data.packet_stats = NULL; self->start_tls_fallback_data.finish = smtp_plug_finish; self->start_tls_fallback_data.timeout_cb = smtp_plug_timeout; ZPlugSession *session = z_plug_session_new(&self->start_tls_fallback_data, self->super.endpoints[EP_CLIENT], self->super.endpoints[EP_SERVER], NULL, &self->super); if (session == NULL) z_proxy_return(self, FALSE); ZPoll *poll = z_poll_new(); if (poll == NULL) { z_plug_session_destroy(session); z_proxy_return(self, FALSE); } z_stream_set_nonblock(self->super.endpoints[EP_CLIENT], TRUE); z_stream_set_nonblock(self->super.endpoints[EP_SERVER], TRUE); if (!z_plug_session_start(session, poll)) { z_plug_session_destroy(session); z_proxy_return(self, FALSE); } while (z_poll_is_running(poll) && self->smtp_state != SMTP_STATE_QUIT) { if (!z_proxy_loop_iteration(&self->super) || !z_poll_iter_timeout(poll, -1)) { self->smtp_state = SMTP_STATE_QUIT; break; } } z_plug_session_cancel(session); z_plug_session_destroy(session); z_poll_unref(poll); z_proxy_return(self, TRUE); } /** * smtp_main: * @self: SmtpProxy instance * * Main function of the SMTP proxy, implements the basic protocol by calling * various functions. **/ static void smtp_main(ZProxy *s) { SmtpProxy *self = Z_CAST(s, SmtpProxy); gboolean success, accepted; gboolean need_quit = FALSE; z_proxy_enter(self); if (!z_proxy_connect_server(&self->super, NULL, 0)) self->proxy_state = SMTP_PROXY_UNCONNECTED_GREET; else self->proxy_state = SMTP_PROXY_RESPONSE; if (!smtp_init_streams(self)) z_proxy_return(self); self->smtp_state = SMTP_STATE_INITIAL; while (self->smtp_state != SMTP_STATE_QUIT) { if (!z_proxy_loop_iteration(s)) { self->smtp_state = SMTP_STATE_QUIT; break; } g_string_assign(self->error_code, "500"); g_string_assign(self->error_info, "Invalid command"); switch (self->proxy_state) { case SMTP_PROXY_UNCONNECTED_GREET: g_string_printf(self->error_code, "%d", self->unconnected_response_code); g_string_assign(self->error_info, "Server not available"); self->proxy_state = SMTP_PROXY_UNCONNECTED_REJECT_ALL; goto error; case SMTP_PROXY_UNCONNECTED_REJECT_ALL: if (!smtp_fetch_request(self)) goto error; switch (smtp_process_request(self)) { case SMTP_REQ_ACCEPT: break; case SMTP_REQ_NOOP: continue; default: goto error; } if (strcasecmp(self->request->str, "QUIT") != 0) { g_string_assign(self->error_code, "503"); g_string_assign(self->error_info, "Server not available"); } else { g_string_assign(self->error_code, "221"); g_string_assign(self->error_info, "Bye"); self->smtp_state = SMTP_STATE_QUIT; } goto error; case SMTP_PROXY_TRANSFER: success = smtp_process_transfer(self); z_object_unref(&self->transfer->super); self->transfer = NULL; self->data_transfer = FALSE; accepted = FALSE; if (success) { if (!smtp_fetch_response(self)) { z_proxy_log(self, SMTP_ERROR, 3, "Error fetching acknowledgement response from server;"); success = FALSE; } else if (!smtp_response_accepted(smtp_process_response(self))) { z_proxy_log(self, SMTP_ERROR, 3, "Error processing acknowledgement response from server;"); success = FALSE; } else if (!smtp_copy_response(self)) { z_proxy_log(self, SMTP_ERROR, 3, "Error sending acknowledgement to client, the message might be delivered multiple times;"); success = FALSE; } else if (self->response->str[0] == '2') { accepted = TRUE; } if (!accepted && success) { z_proxy_log(self, SMTP_RESPONSE, 4, "Server rejected our message; response='%s', response_param='%s'", self->response->str, self->response_param->str); } else if (!success) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; } } /*LOG This message reports the accounting information of the mail transfer. */ if (success) z_proxy_log(self, SMTP_ACCOUNTING, 4, "Accounting; sender='%s', recipient='%s', response='%s', response_param='%s', result='%s'", self->sender->str, self->recipients->str, self->response->str, self->response_param->str, accepted ? "success" : "failure"); else z_proxy_log(self, SMTP_ACCOUNTING, 4, "Accounting; sender='%s', recipient='%s', response='%s', response_param='%s', result='%s'", self->sender->str, self->recipients->str, self->error_code->str, self->error_info->len ? self->error_info->str : "Invalid command", accepted ? "success" : "failure"); smtp_reset_state(self); self->proxy_state = SMTP_PROXY_REQUEST; if (!success) goto error; break; case SMTP_PROXY_REQUEST: if (self->smtp_state != SMTP_STATE_AUTH) { if (!smtp_fetch_request(self)) goto error; switch (smtp_process_request(self)) { case SMTP_REQ_ACCEPT: break; case SMTP_REQ_NOOP: continue; default: goto error; } if (self->data_transfer) { self->transfer = smtp_transfer_new(self); if (!z_transfer2_start(self->transfer)) { z_object_unref(&self->transfer->super); self->transfer = NULL; self->data_transfer = FALSE; g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); need_quit = TRUE; goto error; } } if ((self->data_transfer && !smtp_transfer_is_data_delayed(self->transfer)) || !self->data_transfer) { if (!smtp_copy_request(self)) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; goto error; } } if (self->data_transfer && smtp_transfer_is_data_delayed(self->transfer)) self->proxy_state = SMTP_PROXY_TRANSFER; else self->proxy_state = SMTP_PROXY_RESPONSE; } else { if (!smtp_fetch_auth_request(self) || !smtp_copy_auth_request(self)) goto error; self->proxy_state = SMTP_PROXY_RESPONSE; } break; case SMTP_PROXY_RESPONSE: if (!self->data_transfer) self->proxy_state = SMTP_PROXY_REQUEST; else self->proxy_state = SMTP_PROXY_TRANSFER; self->data_transfer = FALSE; if (!smtp_fetch_response(self)) { g_string_assign(self->error_code, "421"); g_string_assign(self->error_info, "Service not available, closing transmission channel."); self->error_abort = TRUE; goto error; } switch (smtp_process_response(self)) { case SMTP_RSP_ACCEPT: if (!smtp_copy_response(self)) goto error; break; case SMTP_RSP_NOOP: /* smtp_process_response() already did everything, so we don't have to copy the response to the client */ break; default: /* SMTP_RSP_REJECT or SMTP_RSP_ABORT: we go to the error branch, * since the response has been dropped */ goto error; } break; case SMTP_PROXY_DATA: break; case SMTP_PROXY_PLUG: smtp_plug_do_transfer(self); self->smtp_state = SMTP_STATE_QUIT; break; } continue; error: if (self->proxy_state != SMTP_PROXY_UNCONNECTED_REJECT_ALL && !need_quit) self->proxy_state = SMTP_PROXY_REQUEST; if (need_quit) self->proxy_state = SMTP_PROXY_UNCONNECTED_REJECT_ALL; if (self->transfer) { z_transfer2_cancel(self->transfer); z_object_unref(&self->transfer->super); self->transfer = NULL; self->data_transfer = FALSE; } smtp_set_response(self, self->error_code->str, self->error_info->len ? self->error_info->str : "Invalid command"); if (!smtp_copy_response(self) || self->error_abort) break; else continue; } /*LOG This message reports that Zorp is exiting from the SMTP proxy loop and closing connections on both side. */ z_proxy_log(self, SMTP_DEBUG, 6, "Exiting SMTP loop;"); smtp_clear_response(self); z_proxy_return(self); } /** * smtp_proxy_new: * @session_id: session id string * @client: client stream * @handler: policy handler * @parent: parent proxy if applicable * * This function is called by the Zorp core to create a new SMTP proxy * instance. * * Returns: a ZProxy reference which represents the new proxy **/ static ZProxy * smtp_proxy_new(ZProxyParams *params) { SmtpProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(SmtpProxy), params), SmtpProxy); z_return((ZProxy *) self); } /** * smtp_proxy_free: * @s: SmtpProxy instance * * This virtual function overrides z_proxy_free() to also free SMTP specific * fields in @s. **/ static void smtp_proxy_free(ZObject *s) { SmtpProxy *self = Z_CAST(s, SmtpProxy); z_enter(); z_poll_unref(self->poll); g_string_free(self->auth_request, TRUE); g_string_free(self->sanitized_recipient, TRUE); z_proxy_free_method(s); z_return(); } ZProxyFuncs smtp_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), smtp_proxy_free, }, .config = smtp_config, .main = smtp_main, NULL }; Z_CLASS_DEF(SmtpProxy, ZProxy, smtp_proxy_funcs); /** * zorp_module_init: * * Zorp module initialization function. **/ gint zorp_module_init(void) { z_registry_add("smtp", ZR_PROXY, smtp_proxy_new); smtp_init_cmd_hash(); return TRUE; } zorp-3.9.5/modules/smtp/smtp.h000066400000000000000000000165501172670260400163250ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_SMTP_H_INCLUDED #define ZORP_MODULES_SMTP_H_INCLUDED #include "smtpmsg.h" #include #include #include #include #include #include #include #include /* smtp protocol states */ typedef enum { SMTP_STATE_INITIAL = 1 << 0, SMTP_STATE_EHLO = 1 << 1, SMTP_STATE_AUTH = 1 << 2, SMTP_STATE_MAIL_FROM = 1 << 3, SMTP_STATE_RCPT_TO = 1 << 4, SMTP_STATE_DATA = 1 << 5, SMTP_STATE_QUIT = 1 << 6 }SmtpStateTypes; /* smtp proxy states */ typedef enum { SMTP_PROXY_REQUEST = 1, SMTP_PROXY_RESPONSE, SMTP_PROXY_DATA, SMTP_PROXY_UNCONNECTED_GREET, SMTP_PROXY_UNCONNECTED_REJECT_ALL, SMTP_PROXY_TRANSFER, SMTP_PROXY_PLUG }SmtpProxyStateTypes; typedef enum { SMTP_TRANSFER_SUSPEND_DATA = 100, SMTP_TRANSFER_SUSPEND_NOOP }SmtpTransferTypes; /* smtp extensions */ typedef enum { SMTP_EM_PIPELINING = 1 << 0, SMTP_EM_SIZE = 1 << 1, SMTP_EM_ETRN = 1 << 2, SMTP_EM_8BITMIME = 1 << 3, SMTP_EM_AUTH = 1 << 4, SMTP_EM_STARTTLS = 1 << 5 }SmtpExtensionTypes; typedef enum { SMTP_EXT_ACCEPT = 1, SMTP_EXT_DROP = 5 }SmtpActionTypes; typedef enum { SMTP_REQ_ACCEPT = 1, SMTP_REQ_REJECT = 3, SMTP_REQ_ABORT = 4, SMTP_REQ_POLICY = 6, SMTP_REQ_NOOP = 101 }SmtpRequestTypes; typedef enum { SMTP_RSP_ACCEPT = 1, SMTP_RSP_REJECT = 3, SMTP_RSP_ABORT = 4, SMTP_RSP_POLICY = 6, SMTP_RSP_NOOP = 101, }SmtpResponseTypes; #define SMTP_VIOLATION "smtp.violation" #define SMTP_REQUEST "smtp.request" #define SMTP_RESPONSE "smtp.response" #define SMTP_POLICY "smtp.policy" #define SMTP_DEBUG "smtp.debug" #define SMTP_ERROR "smtp.error" #define SMTP_INFO "smtp.info" #define SMTP_ACCOUNTING "smtp.accounting" typedef struct _SmtpProxy SmtpProxy; typedef struct _SmtpTransfer SmtpTransfer; typedef guint (*SmtpCmdFunction)(struct _SmtpProxy *); typedef struct _SmtpCommandDesc { gchar *name; SmtpCmdFunction command_parse; SmtpCmdFunction response_parse; SmtpCmdFunction action_do; SmtpStateTypes smtp_state; } SmtpCommandDesc; typedef struct _SmtpExtensionDesc { gchar *name; guint32 extension_mask; } SmtpExtensionDesc; struct _SmtpProxy { ZProxy super; /* general I/O timeout */ glong timeout; glong interval_transfer_noop; /* specifies which command is allowed, SMTP_STATE_* */ SmtpStateTypes smtp_state; /* whether we should process requests/responses or data SMTP_PROXY_* */ SmtpProxyStateTypes proxy_state; /* permitted SMTP extension */ GHashTable *extensions; /* compatibility: permitted extension set via bitmask */ SmtpActionTypes permit_extensions; /* negotiated SMTP extension set */ SmtpActionTypes active_extensions; /* policy exported variables */ GHashTable *request_policy; ZDimHashTable *response_policy; /* I/O buffer size for data transfer */ gsize buffer_size; /* whether to permit commands not explicitly allowed */ gboolean permit_unknown_command; gboolean permit_long_responses; /* whether to allow MAIL From and RCPT To addresses to appear without surrounding <>-s */ gboolean permit_omission_of_angle_brackets; /* maximum line lengths */ guint max_request_length; guint max_auth_request_length; guint max_response_length; guint max_line_length; gboolean require_crlf; gint unconnected_response_code; /* stores whether or not processing a STARTTLS request is allowed */ gboolean start_tls_ok[EP_MAX]; /* whether or not to fall back to a plugsession if a STARTTLS request is accepted */ gboolean tls_passthrough; ZPlugSessionData start_tls_fallback_data; /* error information to be returned to the client */ /* reply code */ GString *error_code; /* whether to abort the connection */ gboolean error_abort; /* string info appended to the error code */ GString *error_info; GString *append_domain; /* proxy state */ /* current request descriptor */ SmtpCommandDesc *request_cmd; /* current request as string */ GString *request; /* current request parameter */ GString *request_param; /* sender/recipient logging */ GString *sender; /* last, canonicalized recipient */ GString *sanitized_recipient; GString *recipients; GString *auth_request; /* Add receive line */ gboolean add_received_header; /* Received string as parameter or HELO/EHLO */ GString *helo_string; /* Protocol in use (SMTP/ESMTP) */ GString *protocol; /* current response as string */ GString *response; /* current response parameter */ GString *response_param; /* when an extended reply is received additional lines are stored here */ GList *response_lines; ZTransfer2 *transfer; /* set to TRUE when a data transfer begins right after response processing */ gboolean data_transfer; ZPoll *poll; }; extern ZClass SmtpProxy__class; extern SmtpMessage smtp_known_messages[SMTP_N_MSGS]; gboolean smtp_sanitize_address(SmtpProxy *self, GString *result, gchar *path, gboolean empty_path_ok, gchar **final_end); gboolean smtp_sanitize_address(SmtpProxy *self, GString *result, gchar *path, gboolean empty_path_ok, gchar **final_end); gboolean smtp_generate_received(SmtpProxy *self, GString **dst_string); void smtp_reset_state(SmtpProxy *self); SmtpRequestTypes smtp_policy_check_request(SmtpProxy *self); SmtpResponseTypes smtp_policy_check_response(SmtpProxy *self); gboolean smtp_policy_is_extension_permitted(SmtpProxy *self, gchar *extension); ZTransfer2 *smtp_transfer_new(SmtpProxy *self); static inline gboolean smtp_transfer_is_data_delayed(ZTransfer2 *self) { return !!self->stacked; } extern GHashTable *known_commands; extern GHashTable *known_extensions; void smtp_init_cmd_hash(void); gboolean smtp_copy_response(SmtpProxy *self); void smtp_clear_response(SmtpProxy *self); ZPolicyObj *smtp_policy_sanitize_address(SmtpProxy *self, ZPolicyObj *args); #define SMTP_SET_RESPONSE(smtp_msg_type) \ G_STMT_START{ \ smtp_clear_response(self);\ g_string_assign(self->response, smtp_known_messages[smtp_msg_type].code);\ g_string_assign(self->response_param, smtp_known_messages[smtp_msg_type].long_desc);\ }G_STMT_END #endif zorp-3.9.5/modules/smtp/smtpcmd.c000066400000000000000000001000151172670260400167720ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "smtp.h" #include #include #define FROM_ADDR "From:" #define TO_ADDR "To:" /** * smtp_parse_atom: * @self: SmtpProxy instance * @path: forward or reverse path to parse * @end: the character where the parsing ended * * This function parses an 'atom' as defined by RFC821. The return value * indicates whether parsing succeeded, thus it always returns TRUE (as an * atom can be 0 characters in length). **/ static gboolean smtp_parse_atom(SmtpProxy *self G_GNUC_UNUSED, gchar *path, gchar **end) { /* characters excluded from in RFC821, that is, members of and */ gchar specials[] = { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '.', '[', ']', ' ' }; gint i = 0, j; z_proxy_enter(self); while (path[i]) { for (j = 0; j < (gint) sizeof(specials); j++) { if (path[i] == specials[j]) { *end = &path[i]; z_proxy_leave(self); return TRUE; } } i++; } *end = &path[i]; z_proxy_leave(self); return TRUE; } /** * smtp_parse_domain: * @self: SmtpProxy instance * @path: forward or reverse path to parse * @end: the character where the parsing ended * * This function parses a 'domain' as defined by RFC821. The return value * indicates whether parsing succeeded. Regardless of the return value @end * is filled with the character position where parsing ended. **/ static gboolean smtp_parse_domain(SmtpProxy *self, gchar *path, gchar **end) { gchar *src; z_proxy_enter(self); if (path[0] == '#') { IGNORE_UNUSED_RESULT(strtol(&path[1], &src, 10)); *end = src; } else if (path[0] == '[') { /* check domain-literal */ src = path + 1; while (*src) { if (*src == ']') { break; } else if (*src == '"' || *src == '\n') { return FALSE; } else if (*src == '\\') { src++; } src++; } *end = src + 1; } else { src = path; while (*src) { if (!smtp_parse_atom(self, src, &src) || *src != '.') { break; } src++; } *end = src; } z_proxy_leave(self); return src != path; } /** * smtp_parse_source_route: * @self: SmtpProxy instance * @path: forward or reverse path to parse * @end: the character where the parsing ended * * This function parses a 'source-route' as defined by RFC821. The return value * indicates whether parsing succeeded. Regardless of the return value @end * is filled with the character position where parsing ended. **/ static gboolean smtp_parse_source_route(SmtpProxy *self, gchar *path, gchar **end) { gchar *src, *p; gboolean continued = FALSE; z_proxy_enter(self); /* Source route format: @fqdn,fqdn,fqdn: */ src = path; *end = src; /* source route present */ while (*src == '@') { src++; continued = FALSE; if (!smtp_parse_domain(self, src, &p)) { z_proxy_leave(self); return FALSE; } if (*p != ',' && *p != ':') { z_proxy_leave(self); return FALSE; } src = p + 1; *end = src; if (*p == ':') { break; } continued = TRUE; } z_proxy_leave(self); return !continued; } /** * smtp_parse_source_route: * @self: SmtpProxy instance * @path: forward or reverse path to parse * @end: the character where the parsing ended * * This function parses a 'local-part' as defined by RFC821. The return value * indicates whether parsing succeeded. Regardless of the return value @end * is filled with the character position where parsing ended. **/ static gboolean smtp_parse_local_part(SmtpProxy *self, gchar *path, gchar **end) { gchar *src; z_proxy_enter(self); src = path; if (*src == '"') { /* quoted local part */ src = path + 1; while (*src) { if (*src == '"') { break; } else if (*src == '\\') { src++; } src++; } *end = src + 1; } else { /* *atom */ while (*src) { if (!smtp_parse_atom(self, src, &src) || *src != '.') { break; } src++; } *end = src; } z_proxy_leave(self); return src != path; } /** * smtp_parse_source_route: * @self: SmtpProxy instance * @result: the function stores the plain email address here * @path: forward or reverse path to parse * @end: the character where the parsing ended * * This function parses an 'address' as defined by RFC821. The return value * indicates whether parsing succeeded. Regardless of the return value @end * is filled with the character position where parsing ended. If the parsing * was successful the address string is stored in the argument @result. **/ static gboolean smtp_parse_address(SmtpProxy *self, GString *result, gchar *path, gchar **end) { gchar *src = path; gchar *start; z_proxy_enter(self); start = src; *end = src; if (!smtp_parse_local_part(self, src, end)) { /*LOG This message indicates that parsing the local part of the address failed. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Error parsing local part; path='%s'", path); z_proxy_leave(self); return FALSE; } src = *end; if (*src != '@') { if (self->append_domain->len) { g_string_assign_len(result, start, (*end) - start); g_string_sprintfa(result, "@%s", self->append_domain->str); } else { /*LOG This message indicates that the local part of the mail is invalid because it does not end with a '@' sign. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Local part does not end in '@'; path='%s'", path); z_proxy_leave(self); return FALSE; } z_proxy_leave(self); return TRUE; } src++; *end = src; if (!smtp_parse_domain(self, src, end)) { /*LOG This message indicates that the domain name of the mail is invalid. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid domain name in path; path='%s'", path); z_proxy_leave(self); return FALSE; } g_string_assign_len(result, start, (*end) - start); z_proxy_leave(self); return TRUE; } /** * smtp_sanitize_address: * @result: copy the stripped down e-mail address to this string * @path: forward- or reversepath in SMTP sense (the argument to RCPT To: or MAIL From) * @end: the address of the character where processing ended is stored here * * This function is used to strip unneeded source routing information * from reverse- or forward paths. While stripping the unnecessary * bits, the function also validates the incoming string. The argument * @end is useful when SMTP extensions are used, as the set extensions * are starting where @end points to. If @end is NULL, then no trailing * characters are permitted. * * Returns: TRUE to indicate success **/ gboolean smtp_sanitize_address(SmtpProxy *self, GString *result, gchar *path, gboolean empty_path_ok, gchar **final_end) { gchar *src, *end; gboolean res; gboolean unbracketed = FALSE; z_proxy_enter(self); src = path; /* skip spaces */ while (*src == ' ') src++; if (*src != '<' ) { unbracketed = TRUE; if (!self->permit_omission_of_angle_brackets) { /*LOG This message indicates that the address path is invalid because it does not start with a '<' sign. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Path does not start with '<'; path='%s'", path); z_proxy_return(self, FALSE); } } else { src++; /* skip over the < */ } g_string_truncate(result, 0); if (!smtp_parse_source_route(self, src, &end) && src != end) { /*LOG This message indicates that the source-root information of the address patch is invalid. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid source route information; path='%s'", path); z_proxy_return(self, FALSE); } src = end; if (*src != '>' || !empty_path_ok) { if (!smtp_parse_address(self, result, src, &end)) { /*LOG This message indicates that the address information is invalid. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid address information; path='%s'", path); z_proxy_return(self, FALSE); } } src = end; if (unbracketed) { if (*src == '>') { /*LOG This message indicates that the address path is invalid because it does not start with a '>' sign but ends with it. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Path does not begin with '<' but ends with '>'; path='%s'", path); z_proxy_return(self, FALSE); } } else { if (*src == '>') { src++; } else { /*LOG This message indicates that the address path is invalid because it does not end with a '>' sign. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Path begins with '<' but does not end with '>'; path='%s'", path); z_proxy_return(self, FALSE); } } if (final_end) { *final_end = src; res = TRUE; } else { res = (*src == 0); } z_proxy_return(self, res); } /** * smtp_is_domain: * @self: SmtpProxy instance * @domain: domain to check * * This function checks whether the argument is a valid domain name. * * Returns: TRUE to indicate success **/ static gboolean smtp_is_domain(SmtpProxy *self, gchar *domain) { gchar *end; if (smtp_parse_domain(self, domain, &end) && *end == '\0') return TRUE; return FALSE; } /** * smtp_is_queue_tag: * @self: SmtpProxy instance * @tag: tag to verify * * This function validates an SMTP queue tag when the '#' form of ETRN is * used. * * Returns: TRUE to indicate success **/ static gboolean smtp_is_queue_tag(SmtpProxy *self G_GNUC_UNUSED, gchar *tag) { gchar *p = tag; while (*p) { if (!(isalpha(*p) || *p == '-' || (*p >= '0' && *p <= '9') || *p == '.' || *p == '_')) return FALSE; p++; } return TRUE; } /** * smtp_is_xtext: self: SmtpProxy instance xtext: tag to verify * * This function validates an SMTP xtext, which is a string consisting of * characters in the range [33-126] inclusive, other characters are encoded * using two hexadecimal characters. * * Returns: TRUE to indicate success **/ static gboolean smtp_is_xtext(SmtpProxy *self G_GNUC_UNUSED, gchar *xtext) { const guchar *p = (const guchar *) xtext; while (*p) { if (*p < 33 || *p > 126 || *p == '=') return FALSE; else if (*p == '+') { /* hexadecimal encoding */ if (!(*(p+1)) || !(*(p+2)) || !isxdigit(*(p+1)) || !isxdigit(*(p+2))) return FALSE; p += 2; } p++; } return TRUE; } /** * smtp_parse_mail_extensions: * @self: SmtpProxy instance * @ext: MAIL extensions * * This function is called when the MAIL command contains extensions that * need to be parsed. Currently the only such extension is SIZE, everything else * causes an error. * * Returns: TRUE to indicate success **/ static gboolean smtp_parse_mail_extensions(SmtpProxy *self, gchar *ext, GString *forward_extensions) { gchar *p; gchar kw[32], val[256]; guint kw_len, val_len; z_proxy_enter(self); g_string_truncate(forward_extensions, 0); p = ext; while (*p == ' ') p++; while (*p) { /* skip whitespace */ kw_len = 0; while ((isalpha(*p) || isdigit(*p)) && kw_len < sizeof(kw) - 1) { kw[kw_len++] = *p; p++; } kw[kw_len] = 0; if (*p != '=') z_proxy_return(self, FALSE); p++; val_len = 0; while (p && *p != ' ' && *p != '=' && *p > 32 && *p < 127 && val_len < sizeof(val) - 1) { val[val_len++] = *p; p++; } val[val_len] = 0; if ((self->active_extensions & SMTP_EM_SIZE) && strcasecmp(kw, "SIZE") == 0) { gchar *end; gulong size; size = strtol(val, &end, 10); if (*end != 0) { /*LOG This message indicates that the SIZE extension of the MAIL command is invalid because it must contain non-numeric characters. Zorp rejects the request. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid SIZE extension in the MAIL command; extensions='%s'", ext); z_proxy_return(self, FALSE); } g_string_sprintfa(forward_extensions, "SIZE=%lu ", size); } else if ((self->active_extensions & SMTP_EM_8BITMIME) && strcasecmp(kw, "BODY") == 0) { if (strcasecmp(val, "7BIT") != 0 && strcasecmp(val, "8BITMIME") != 0) { /*LOG This message indicates that the BODY extension of the MAIL command is invalid because it must contain either '7BIT' or '8BITMIME'. Zorp rejects the request. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid BODY extension in the MAIL command; extensions='%s'", ext); z_proxy_return(self, FALSE); } g_string_sprintfa(forward_extensions, "BODY=%s ", val); } else if ((self->active_extensions & SMTP_EM_AUTH) && strcasecmp(kw, "AUTH") == 0) { if (!smtp_is_xtext(self, val)) { /*LOG This message indicates that the AUTH extension of the MAIL command is invalid because it must be xtext. Zorp rejects the request. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid AUTH sender, not an xtext; extensions='%s'", ext); z_proxy_return(self, FALSE); } g_string_sprintfa(forward_extensions, "AUTH=%s ", val); } else { /*LOG This message indicates that the given extension is invalid with the MAIL command and Zorp rejects the request. */ z_proxy_log(self, SMTP_VIOLATION, 2, "Invalid extension in the MAIL command; extensions='%s'", ext); z_proxy_return(self, FALSE); } while (*p == ' ') p++; } /* strip trailing spaces */ p = forward_extensions->str + forward_extensions->len - 1; while (p > forward_extensions->str && *p == ' ') { *p = '\0'; p--; forward_extensions->len--; } z_proxy_return(self, TRUE); } SmtpRequestTypes smtp_request_EHLO(SmtpProxy *self) { g_string_assign(self->helo_string, self->request_param->str); g_string_assign(self->protocol, strcmp(self->request->str, "HELO") ? "ESMTP" : "SMTP"); return smtp_is_domain(self, self->request_param->str) ? SMTP_REQ_ACCEPT : SMTP_REQ_REJECT; } SmtpResponseTypes smtp_response_EHLO(SmtpProxy *self) { self->active_extensions = 0; if (self->response_lines && strcmp(self->request->str, "HELO") == 0) { /* extended response for a HELO */ return SMTP_RSP_REJECT; } else if (self->response_lines) { GList *p, *pnext; for (p = self->response_lines; p; p = pnext) { gchar token[256], *dst = token; const gchar *src; gboolean remove_ext_from_list = TRUE; SmtpExtensionDesc *ext; for (src = ((GString *) p->data)->str; (dst - token) < (gint) sizeof(token) - 1 && isalnum(*src); src++, dst++) { /* make extension id upper case */ *dst = toupper(*src); } *dst = 0; pnext = p->next; if (smtp_policy_is_extension_permitted(self, token)) { remove_ext_from_list = FALSE; ext = g_hash_table_lookup(known_extensions, token); if (ext) { /* this is a known extension */ self->active_extensions |= ext->extension_mask; if (ext->extension_mask & SMTP_EM_STARTTLS) { /* we also have some hard-coded rules depending on SSL settings: * - client: != ACCEPT_STARTTLS / server *: we have to remove 'STARTTLS' * - client: ACCEPT_STARTTLS / server != FORWARD_STARTTLS: we have to add 'STARTTLS' */ if (self->super.ssl_opts.security[EP_CLIENT] != PROXY_SSL_SEC_ACCEPT_STARTTLS || self->start_tls_ok[EP_CLIENT]) { self->active_extensions &= ~SMTP_EM_STARTTLS; remove_ext_from_list = TRUE; } else if (self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS && self->super.ssl_opts.security[EP_SERVER] != PROXY_SSL_SEC_FORWARD_STARTTLS && !self->start_tls_ok[EP_CLIENT]) { self->active_extensions |= SMTP_EM_STARTTLS; } } } } if (remove_ext_from_list) { /* not permitted extension */ g_string_free((GString *) p->data, TRUE); self->response_lines = g_list_remove_link(self->response_lines, p); g_list_free_1(p); } } } if (self->response->str[0] == '2') { self->smtp_state = SMTP_STATE_EHLO; } return SMTP_RSP_ACCEPT; } SmtpRequestTypes smtp_request_AUTH(SmtpProxy *self) { SmtpRequestTypes res = SMTP_REQ_REJECT; z_proxy_enter(self); if (self->active_extensions & SMTP_EM_AUTH) res = SMTP_REQ_ACCEPT; z_proxy_return(self, res); } SmtpResponseTypes smtp_response_AUTH(SmtpProxy *self) { SmtpResponseTypes res = SMTP_RSP_ACCEPT; if (strcmp(self->response->str, "334") == 0) self->smtp_state = SMTP_STATE_AUTH; else if (strcmp(self->response->str, "235") == 0) self->smtp_state = SMTP_STATE_EHLO; /* authentication was successful, go on receiving mail */ else if (self->response->str[0] == '4' || self->response->str[0] == '5') self->smtp_state = SMTP_STATE_EHLO; /* authentication was aborted, return to normal state */ else res = SMTP_RSP_REJECT; return res; } SmtpRequestTypes smtp_request_MAIL(SmtpProxy *self) { GString *sanitized_address, *forward_extensions = NULL; gchar *end; z_proxy_enter(self); if (g_ascii_strncasecmp(self->request_param->str, FROM_ADDR, strlen(FROM_ADDR)) == 0) { sanitized_address = g_string_sized_new(128); if (smtp_sanitize_address(self, sanitized_address, &self->request_param->str[strlen(FROM_ADDR)], TRUE, &end)) { if (*end) forward_extensions = g_string_sized_new(strlen(end) + 1); if (*end == 0 || smtp_parse_mail_extensions(self, end, forward_extensions)) { g_string_sprintf(self->request_param, "%s<%s>%s%s", FROM_ADDR, sanitized_address->str, forward_extensions ? " " : "", forward_extensions ? forward_extensions->str : ""); g_string_assign(self->sender, sanitized_address->str); g_string_free(sanitized_address, TRUE); if (forward_extensions) g_string_free(forward_extensions, TRUE); z_proxy_return(self, SMTP_REQ_ACCEPT); } if (forward_extensions) g_string_free(forward_extensions, TRUE); } g_string_free(sanitized_address, TRUE); } z_proxy_return(self, SMTP_REQ_REJECT); } SmtpResponseTypes smtp_response_MAIL(SmtpProxy *self) { if (self->response->str[0] == '2') { self->smtp_state = SMTP_STATE_MAIL_FROM; z_proxy_log(self, SMTP_INFO, 4, "Server accepted the sender; sender='%s', response='%s', response_param='%s'", self->sender->str, self->response->str, self->response_param->str); } else if (self->response->str[0] == '4' || self->response->str[0] == '5') { z_proxy_log(self, SMTP_ERROR, 3, "Server rejected the sender; sender='%s', response='%s', response_param='%s'", self->sender->str, self->response->str, self->response_param->str); } return SMTP_RSP_ACCEPT; } SmtpRequestTypes smtp_request_RCPT(SmtpProxy *self) { z_proxy_enter(self); if (g_ascii_strncasecmp(self->request_param->str, TO_ADDR, strlen(TO_ADDR)) == 0) { if (smtp_sanitize_address(self, self->sanitized_recipient, &self->request_param->str[strlen(TO_ADDR)], FALSE, NULL)) { g_string_sprintf(self->request_param, "%s<%s>", TO_ADDR, self->sanitized_recipient->str); z_proxy_return(self, SMTP_REQ_ACCEPT); } } z_proxy_return(self, SMTP_REQ_REJECT); } SmtpResponseTypes smtp_response_RCPT(SmtpProxy *self) { if (self->response->str[0] == '2') { if (self->recipients->len) g_string_sprintfa(self->recipients, " %s", self->sanitized_recipient->str); else g_string_append(self->recipients, self->sanitized_recipient->str); self->smtp_state = SMTP_STATE_RCPT_TO; z_proxy_log(self, SMTP_INFO, 4, "Server accepted the recipient; recipient='%s', response='%s', response_param='%s'", self->sanitized_recipient->str, self->response->str, self->response_param->str); } else if (self->response->str[0] == '4' || self->response->str[0] == '5') { z_proxy_log(self, SMTP_ERROR, 3, "Server rejected the recipient; recipient='%s', response='%s', response_param='%s'", self->sanitized_recipient->str, self->response->str, self->response_param->str); } return SMTP_RSP_ACCEPT; } SmtpRequestTypes smtp_request_ETRN(SmtpProxy *self) { if (self->active_extensions & SMTP_EM_ETRN && self->request_param->len > 0) { gchar *p = self->request_param->str; if (self->request_param->str[0] == '@') { /* domain name with subdomains */ if (smtp_is_domain(self, p+1)) return SMTP_REQ_ACCEPT; } else if (self->request_param->str[0] == '#') { /* queue tag */ if (smtp_is_queue_tag(self, p+1)) return SMTP_REQ_ACCEPT; } else { /* fqdn */ if (smtp_is_domain(self, p)) return SMTP_REQ_ACCEPT; } } return SMTP_REQ_REJECT; } SmtpResponseTypes smtp_response_RSET(SmtpProxy *self) { z_proxy_enter(self); if (self->response->str[0] == '2') smtp_reset_state(self); z_proxy_return(self, SMTP_RSP_ACCEPT); } SmtpRequestTypes smtp_request_DATA(SmtpProxy *self) { z_proxy_enter(self); self->data_transfer = TRUE; #if 0 if (self->response->str[0] == '3') self->data_transfer = TRUE; else if (self->response->str[0] == '2') self->smtp_state = SMTP_STATE_EHLO; #endif z_proxy_return(self, SMTP_REQ_ACCEPT); } SmtpRequestTypes smtp_request_general_noarg(SmtpProxy *self) { SmtpRequestTypes res; z_proxy_enter(self); res = self->request_param->len == 0 ? SMTP_REQ_ACCEPT : SMTP_REQ_REJECT; z_proxy_return(self, res); } SmtpResponseTypes smtp_response_QUIT(SmtpProxy *self) { self->smtp_state = SMTP_STATE_QUIT; return SMTP_RSP_ACCEPT; } static SmtpRequestTypes smtp_request_STARTTLS(SmtpProxy *self) { z_proxy_enter(self); if (smtp_request_general_noarg(self) != SMTP_REQ_ACCEPT) goto error; if ((self->active_extensions & SMTP_EM_STARTTLS) == 0) { z_proxy_log(self, SMTP_VIOLATION, 4, "Server does not support the STARTTLS extension;"); goto error; } if (self->start_tls_ok[EP_CLIENT] == TRUE) { z_proxy_log(self, SMTP_VIOLATION, 3, "STARTTLS command is allowed only in plain-text mode;"); goto error; } /* based on the client/server SSL settings, we do the following: * - client ACCEPT_STARTTLS / server FORWARD_STARTTLS: we forward * the request as it is * - client !ACCEPT_STARTTLS / server *: we reject the request * - client ACCEPT_STARTTLS / server !FORWARD_STARTTLS: return success * to the client and don't forward the request */ switch (self->super.ssl_opts.security[EP_CLIENT]) { case PROXY_SSL_SEC_FORWARD_STARTTLS: g_assert_not_reached(); case PROXY_SSL_SEC_NONE: z_proxy_log(self, SMTP_POLICY, 4, "Client-side STARTTLS is not permitted by policy;"); goto error; case PROXY_SSL_SEC_FORCE_SSL: SMTP_SET_RESPONSE(SMTP_MSG_TLS_NOT_AVAILABLE); goto error; case PROXY_SSL_SEC_ACCEPT_STARTTLS: switch (self->super.ssl_opts.security[EP_SERVER]) { case PROXY_SSL_SEC_ACCEPT_STARTTLS: g_assert_not_reached(); case PROXY_SSL_SEC_FORWARD_STARTTLS: break; case PROXY_SSL_SEC_NONE: case PROXY_SSL_SEC_FORCE_SSL: /* Nothing to do. SSL handshake made as action */ break; } break; } z_proxy_return(self, SMTP_REQ_ACCEPT); error: z_proxy_return(self, SMTP_REQ_REJECT); } static SmtpResponseTypes smtp_response_STARTTLS(SmtpProxy *self) { z_proxy_enter(self); /* we can get here only in the following case, all others should have been handled in * parser of the starttls command * - client ACCEPT_STARTTLS / server FORWARD_STARTTLS: do handshake on both * sides if the server accepted the request */ g_assert((self->super.ssl_opts.security[EP_CLIENT] == PROXY_SSL_SEC_ACCEPT_STARTTLS) && (self->super.ssl_opts.security[EP_SERVER] == PROXY_SSL_SEC_FORWARD_STARTTLS)); if (atoi(self->response->str) != 220) z_proxy_return(self, SMTP_RSP_ACCEPT); z_proxy_log(self, SMTP_INFO, 3, "Server accepted STARTTLS request, starting handshake;"); if (!smtp_copy_response(self)) goto error; if (self->tls_passthrough) { /* Fall-back to plug requested, do not start handshake but transition to the PLUG state */ z_proxy_log(self, SMTP_INFO, 3, "STARTTLS accepted by server, switching to plug mode;"); self->proxy_state = SMTP_PROXY_PLUG; } else { /* Do server and client handshake */ if (!z_proxy_ssl_request_handshake(&self->super, EP_SERVER, FALSE)) { z_proxy_log(self, SMTP_ERROR, 2, "Server-side SSL handshake failed, terminating session;"); goto error; } if (!z_proxy_ssl_request_handshake(&self->super, EP_CLIENT, FALSE)) { z_proxy_log(self, SMTP_ERROR, 2, "Client-side SSL handshake failed, terminating session;"); goto error; } self->start_tls_ok[EP_CLIENT] = self->start_tls_ok[EP_SERVER] = TRUE; /* go back to the initial state, so that we force the client to do a HELO/EHLO again before doing anything else */ self->smtp_state = SMTP_STATE_INITIAL; } /* We've already copied the response to the client, so instead of returning SMTP_RSP_ACCEPT we return _NOOP to signal that the response must not be copied again. */ z_proxy_return(self, SMTP_RSP_NOOP); error: self->smtp_state = SMTP_STATE_QUIT; z_proxy_return(self, SMTP_RSP_NOOP); } static SmtpRequestTypes smtp_action_STARTTLS(SmtpProxy *self) { SmtpRequestTypes res = SMTP_REQ_ACCEPT; z_proxy_enter(self); switch (self->super.ssl_opts.security[EP_SERVER]) { case PROXY_SSL_SEC_ACCEPT_STARTTLS: g_assert_not_reached(); case PROXY_SSL_SEC_FORWARD_STARTTLS: break; case PROXY_SSL_SEC_NONE: case PROXY_SSL_SEC_FORCE_SSL: /* return success to the client right away */ z_proxy_log(self, SMTP_INFO, 3, "Zorp is configured for client-only SMTP STARTTLS, accepting request;"); SMTP_SET_RESPONSE(SMTP_MSG_READY_TO_STARTTLS); if (!smtp_copy_response(self)) goto error; if (!z_proxy_ssl_request_handshake(&self->super, EP_CLIENT, FALSE)) { z_proxy_log(self, SMTP_ERROR, 2, "Client-side SSL handshake failed, terminating session;"); self->start_tls_ok[EP_CLIENT] = FALSE; self->smtp_state = SMTP_STATE_QUIT; res = SMTP_REQ_NOOP; } else { self->start_tls_ok[EP_CLIENT] = TRUE; res = SMTP_REQ_NOOP; } break; } z_proxy_return(self, res); error: self->smtp_state = SMTP_STATE_QUIT; z_proxy_return(self, SMTP_REQ_REJECT); } static struct _SmtpCommandDesc known_commands_table[] = { { "HELO", smtp_request_EHLO, smtp_response_EHLO, NULL, SMTP_STATE_INITIAL | SMTP_STATE_EHLO }, { "EHLO", smtp_request_EHLO, smtp_response_EHLO, NULL, SMTP_STATE_INITIAL | SMTP_STATE_EHLO }, { "MAIL", smtp_request_MAIL, smtp_response_MAIL, NULL, SMTP_STATE_EHLO }, { "RCPT", smtp_request_RCPT, smtp_response_RCPT, NULL, SMTP_STATE_MAIL_FROM | SMTP_STATE_RCPT_TO }, { "DATA", smtp_request_DATA, NULL, NULL, SMTP_STATE_RCPT_TO }, { "ETRN", smtp_request_ETRN, NULL, NULL, SMTP_STATE_EHLO }, { "AUTH", smtp_request_AUTH, smtp_response_AUTH, NULL, SMTP_STATE_EHLO }, { "RSET", smtp_request_general_noarg, smtp_response_RSET, NULL, SMTP_STATE_INITIAL | SMTP_STATE_EHLO | SMTP_STATE_MAIL_FROM | SMTP_STATE_RCPT_TO | SMTP_STATE_DATA }, { "QUIT", smtp_request_general_noarg, smtp_response_QUIT, NULL, SMTP_STATE_INITIAL | SMTP_STATE_EHLO | SMTP_STATE_MAIL_FROM | SMTP_STATE_RCPT_TO | SMTP_STATE_DATA }, { "STARTTLS", smtp_request_STARTTLS, smtp_response_STARTTLS, smtp_action_STARTTLS, SMTP_STATE_EHLO }, { NULL, NULL, NULL, NULL, 0 } }; static struct _SmtpExtensionDesc known_extensions_table[] = { { "PIPELINING", SMTP_EM_PIPELINING }, { "SIZE", SMTP_EM_SIZE }, { "ETRN", SMTP_EM_ETRN }, { "8BITMIME", SMTP_EM_8BITMIME }, { "AUTH", SMTP_EM_AUTH }, { "STARTTLS", SMTP_EM_STARTTLS }, { NULL, 0 } }; GHashTable *known_commands; GHashTable *known_extensions; /** * smtp_init_cmd_hash: * * Called at module initialization time to fill the known commands hash. */ void smtp_init_cmd_hash(void) { gint i; /* known_commands is always looked up with an uppercase command */ known_commands = g_hash_table_new(g_str_hash, g_str_equal); i = 0; while (known_commands_table[i].name != NULL) { g_hash_table_insert(known_commands, known_commands_table[i].name, &known_commands_table[i]); i++; } known_extensions = g_hash_table_new(g_str_hash, g_str_equal); i = 0; while (known_extensions_table[i].name != NULL) { g_hash_table_insert(known_extensions, known_extensions_table[i].name, &known_extensions_table[i]); i++; } } zorp-3.9.5/modules/smtp/smtpdata.c000066400000000000000000000255541172670260400171560ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "smtp.h" #include #include #define SMTP_SR_INITIAL 0 #define SMTP_SR_DATA 1 #define SMTP_DW_INITIAL 0 #define SMTP_DW_TRANSFER 3 #define SMTP_DW_TRANSFER_LF 4 #define SMTP_DW_TRANSFER_DOT 5 #define SMTP_DW_REJECTED 6 /** * SmtpTransfer: * * An SMTP specific transfer class. **/ struct _SmtpTransfer { ZTransfer2 super; /* destination write state */ gint dst_write_state; gint src_read_state; GString *received_line; guint received_line_pos; /* The previous line was too long, it must be concatenated with the current line */ gboolean previous_line_split; }; extern ZClass SmtpTransfer__class; /** * smtp_transfer_src_read: * @s: ZTransfer instance * @stream: stream to read from * @buf: buffer to read into * @buf_len: size of the buffer * @bytes_read: the number of read bytes is returned here * @err: GLib error code * * Reads the incoming data stream, checks for EOF (a single '.' on its own), * removes '.' escaping and handles lines longer than the buffer size of ZStreamLine. * **/ static GIOStatus smtp_transfer_src_read(ZTransfer2 *s G_GNUC_UNUSED, ZStream *stream, gchar *buf, gsize buf_len, gsize *bytes_read, GError **err) { SmtpTransfer *self = Z_CAST(s, SmtpTransfer); SmtpProxy *owner = Z_CAST(self->super.owner, SmtpProxy); GIOStatus res; gsize line_len = buf_len - 2; /* to make space to closing CR LF */ if (G_UNLIKELY(self->src_read_state == SMTP_SR_INITIAL)) { if (owner->add_received_header) { if (self->received_line == NULL) { if (!smtp_generate_received(owner, &self->received_line)) self->src_read_state = SMTP_SR_DATA; } if (self->received_line) { *bytes_read = MIN(buf_len, self->received_line->len - self->received_line_pos); memmove(buf, self->received_line->str + self->received_line_pos, *bytes_read); self->received_line_pos += *bytes_read; if (self->received_line_pos >= self->received_line->len) { self->src_read_state = SMTP_SR_DATA; } return G_IO_STATUS_NORMAL; } } else { self->src_read_state = SMTP_SR_DATA; } } if (buf_len < 2) { return G_IO_STATUS_AGAIN; } res = z_stream_line_get_copy(stream, buf, &line_len, err); if (res == G_IO_STATUS_NORMAL) { if (!self->previous_line_split && line_len > 0 && buf[0] == '.') { if (line_len == 1) { return G_IO_STATUS_EOF; } else { /* strip off first dot */ memmove(buf, &buf[1], line_len - 1); line_len = line_len - 1; } } buf[line_len] = '\r'; buf[line_len+1] = '\n'; *bytes_read = line_len + 2; self->previous_line_split = FALSE; } else if (res == G_IO_STATUS_AGAIN && line_len > 0) { /* streamline indicates that the line was too long, do not add EOL */ *bytes_read = line_len; self->previous_line_split = TRUE; res = G_IO_STATUS_NORMAL; } return res; } /** * smtp_transfer_dst_write: * @s: ZTransfer instance * @stream: stream to write to * @buf: buffer to read into * @count: buffer size * @bytes_read: number of bytes returned * @err: GLib error * * This function handles the data stream as it comes out of the stacked * proxy. It takes care about prefixing the mail body with a "DATA" command, * and through complicated means also takes care about fetching the response * to it. When this is successful it takes care about sending the data * stream reescaping unescaped lines beginning with '.'. **/ static GIOStatus smtp_transfer_dst_write(ZTransfer2 *s, ZStream *stream, const gchar *buf, gsize count, gsize *bytes_written, GError **err) { SmtpTransfer *self = Z_CAST(s, SmtpTransfer); GIOStatus res; GError *local_error = NULL; gsize bw; gsize i; *bytes_written = 0; if (self->dst_write_state == SMTP_DW_INITIAL) { z_transfer2_suspend(s, SMTP_TRANSFER_SUSPEND_DATA); self->dst_write_state = SMTP_DW_TRANSFER; return G_IO_STATUS_AGAIN; } transfer_state: if (self->dst_write_state == SMTP_DW_TRANSFER || self->dst_write_state == SMTP_DW_TRANSFER_LF) { for (i = *bytes_written; i < count; i++) { if (self->dst_write_state == SMTP_DW_TRANSFER) { if (buf[i] == '\n') { self->dst_write_state = SMTP_DW_TRANSFER_LF; } } else if (self->dst_write_state == SMTP_DW_TRANSFER_LF) { if (buf[i] == '.') { /* we need to escape this '.' */ /* first, write buf up to this '.' */ res = z_stream_write(stream, buf + *bytes_written, i - *bytes_written, &bw, &local_error); if (res == G_IO_STATUS_NORMAL && (i - *bytes_written) == bw) { *bytes_written += bw; self->dst_write_state = SMTP_DW_TRANSFER_DOT; break; } else { /* we wrote less bytes, go back to the original state */ self->dst_write_state = SMTP_DW_TRANSFER; *bytes_written += bw; if (local_error) g_propagate_error(err, local_error); return res; } } self->dst_write_state = SMTP_DW_TRANSFER; } } if (i == count) { /* no need to escape */ res = z_stream_write(stream, buf + *bytes_written, count - *bytes_written, &bw, err); *bytes_written += bw; return res; } } if (self->dst_write_state == SMTP_DW_TRANSFER_DOT) { res = z_stream_write(stream, ".", 1, &bw, &local_error); if (res == G_IO_STATUS_NORMAL && bw == 1) { self->dst_write_state = SMTP_DW_TRANSFER; goto transfer_state; } if (local_error) g_propagate_error(err, local_error); return res; } /* server responded non-354 to the DATA command */ return G_IO_STATUS_ERROR; } /** * smtp_transfer_dst_shutdown: * @s: ZTransfer instance * @stream: stream to shut down * @shutdown_mode: shutdown mode * @err: GLib error * * This function is called when the server side stream is to be shut down. It takes care * about ending the mail body with a '.', or **/ static GIOStatus smtp_transfer_dst_shutdown(ZTransfer2 *s G_GNUC_UNUSED, ZStream *stream, GError **err) { gsize bytes_written; GError *local_error = NULL; GIOStatus res = G_IO_STATUS_NORMAL; SmtpTransfer *self = Z_CAST(s, SmtpTransfer); if (self->dst_write_state != SMTP_DW_INITIAL) { res = z_stream_write(stream, "\r\n.\r\n", 5, &bytes_written, &local_error); } if (local_error) g_propagate_error(err, local_error); return res; } static gboolean smtp_transfer_stack_proxy(ZTransfer2 *s, ZStackedProxy **stacked) { SmtpProxy *owner = Z_CAST(s->owner, SmtpProxy); ZPolicyObj *stacked_proxy; gboolean called; gboolean success = TRUE; z_policy_lock(owner->super.thread); stacked_proxy = z_policy_call(owner->super.handler, "requestStack", NULL, &called, owner->super.session_id); if (!stacked_proxy) success = FALSE; else if (stacked_proxy != z_policy_none) { success = z_proxy_stack_object(&owner->super, stacked_proxy, stacked, NULL); } z_policy_var_unref(stacked_proxy); z_policy_unlock(owner->super.thread); return success; } static gboolean smtp_transfer_setup(ZTransfer2 *s) { z_stream_line_set_split(s->endpoints[EP_CLIENT], TRUE); z_stream_line_set_truncate(s->endpoints[EP_CLIENT], FALSE); return TRUE; } static gboolean smtp_transfer_progress(ZTransfer2 *s) { SmtpTransfer *self = Z_CAST(s, SmtpTransfer); if (self->dst_write_state == SMTP_DW_INITIAL) z_transfer2_suspend(s, SMTP_TRANSFER_SUSPEND_NOOP); return TRUE; } static void smtp_transfer_free_method(ZObject *s) { SmtpTransfer *self = Z_CAST(s, SmtpTransfer); if (self->received_line) g_string_free(self->received_line, TRUE); z_transfer2_free_method(s); } ZTransfer2Funcs smtp_transfer_funcs = { { Z_FUNCS_COUNT(ZTransfer2), smtp_transfer_free_method, }, smtp_transfer_src_read, smtp_transfer_dst_write, NULL, smtp_transfer_dst_shutdown, smtp_transfer_stack_proxy, .setup = smtp_transfer_setup, .run = NULL, .progress = smtp_transfer_progress }; Z_CLASS_DEF(SmtpTransfer, ZTransfer2, smtp_transfer_funcs); /** * smtp_transfer_new: * @self: SmtpProxy instance * * This function is an Smtp specific constructor for the ZTransfer2 class. * * Returns: ZTransfer2 instance **/ ZTransfer2 * smtp_transfer_new(SmtpProxy *owner) { SmtpTransfer *self; self = Z_CAST(z_transfer2_new(Z_CLASS(SmtpTransfer), &owner->super, owner->poll, owner->super.endpoints[EP_CLIENT], owner->super.endpoints[EP_SERVER], owner->buffer_size, owner->timeout, ZT2F_COMPLETE_COPY), SmtpTransfer); z_transfer2_set_content_format(&self->super, "email"); z_transfer2_enable_progress(&self->super, owner->interval_transfer_noop); return &self->super; } zorp-3.9.5/modules/smtp/smtpmsg.c000066400000000000000000000027541172670260400170300ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Szilárd Pfeiffer * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "smtpmsg.h" // Hard-coded answers SmtpMessage smtp_known_messages[SMTP_N_MSGS] = { {"220", "Ready to start TLS"}, {"501", "Syntax error (no parameters allowed)"}, {"454", "TLS not available due to temporary reason"} }; zorp-3.9.5/modules/smtp/smtpmsg.h000066400000000000000000000026631172670260400170340ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_SMTP_SMTPMSG_H #define ZORP_MODULES_SMTP_SMTPMSG_H typedef struct { char *code; char *long_desc; }SmtpMessage; typedef enum { SMTP_MSG_READY_TO_STARTTLS, SMTP_MSG_SYNTAX_ERROR_NO_PARAM, SMTP_MSG_TLS_NOT_AVAILABLE, SMTP_N_MSGS }SmtpMessageTypes; #endif zorp-3.9.5/modules/smtp/smtppolicy.c000066400000000000000000000245041172670260400175360ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Attila SZALAY * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "smtp.h" gboolean smtp_hash_get_type(ZPolicyObj *tuple, guint *filter_type) { ZPolicyObj *tmp; if (!z_policy_seq_check(tuple)) { if (z_policy_var_parse(tuple, "i", filter_type)) return TRUE; /* not a sequence */ return FALSE; } tmp = z_policy_seq_getitem(tuple, 0); if (!z_policy_var_parse(tmp, "i", filter_type)) { /* policy syntax error */ z_policy_var_unref(tmp); return FALSE; } z_policy_var_unref(tmp); return TRUE; } SmtpRequestTypes smtp_policy_check_request(SmtpProxy *self) { ZPolicyObj *entry; ZPolicyObj *res; ZPolicyObj *process_cmd = NULL; SmtpRequestTypes action; gchar *response = NULL, *response_param = NULL; z_proxy_enter(self); entry = g_hash_table_lookup(self->request_policy, self->request->str); if (!entry) entry = g_hash_table_lookup(self->request_policy, "*"); if (!entry) z_proxy_return(self, SMTP_REQ_REJECT); z_policy_lock(self->super.thread); if (!smtp_hash_get_type(entry, &action)) { /*LOG This message indicates that the policy type is invalid for the given request and Zorp aborts the connection. Check the 'request' attribute. */ z_proxy_log(self, SMTP_POLICY, 1, "Invalid request policy type; request='%s'", self->request->str); z_policy_unlock(self->super.thread); z_proxy_return(self, SMTP_REQ_ABORT); } z_policy_unlock(self->super.thread); z_cp(); switch (action) { case SMTP_REQ_REJECT: z_policy_lock(self->super.thread); if (!z_policy_var_parse_tuple(entry, "i|ss", &action, &response, &response_param)) { /*LOG This message indicates that the parameter of the request policy of the given request is invalid and Zorp aborts the connection. Check the 'request' attribute. */ z_proxy_log(self, SMTP_POLICY, 1, "Error in request policy; request='%s'", self->request->str); action = SMTP_REQ_ABORT; } else { if (response) g_string_assign(self->error_code, response); if (response_param) g_string_assign(self->error_info, response_param); } z_policy_unlock(self->super.thread); break; case SMTP_REQ_ACCEPT: break; case SMTP_REQ_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(entry, "(iO)", &action, &process_cmd)) { /*LOG This message indicates that the parameter of the request policy of the given request is invalid and Zorp aborts the connection. Check the 'request' attribute. */ z_proxy_log(self, SMTP_POLICY, 1, "Error in request policy; request='%s'", self->request->str); action = SMTP_REQ_ABORT; } else { res = z_policy_call_object(process_cmd, z_policy_var_build("(ss)", self->request->str, self->request_param->str), self->super.session_id); if (res) { if (!z_policy_var_parse(res, "i", &action)) { /*LOG This message indicates that the returned value of the callback for the given request policy is invalid and Zorp aborts the connection. Check the callback function. */ z_proxy_log(self, SMTP_POLICY, 1, "The verdict returned by the policy is not an int; request='%s'", self->request->str); action = SMTP_REQ_ABORT; } else { switch (action) { case SMTP_REQ_ACCEPT: case SMTP_REQ_REJECT: case SMTP_REQ_ABORT: break; default: action = SMTP_REQ_ABORT; break; } } } else { action = SMTP_REQ_ABORT; } } z_policy_unlock(self->super.thread); break; case SMTP_REQ_ABORT: default: action = SMTP_REQ_ABORT; break; } z_proxy_return(self, action); } SmtpResponseTypes smtp_policy_check_response(SmtpProxy *self) { ZPolicyObj *entry, *process_rsp, *res; gchar *key[2]; gchar *response, *response_param; SmtpResponseTypes action; z_proxy_enter(self); if (self->request->len) key[0] = self->request->str; else key[0] = "Null"; key[1] = self->response->str; entry = z_dim_hash_table_search(self->response_policy, 2, key); if (!entry) z_proxy_return(self, SMTP_RSP_REJECT); z_policy_lock(self->super.thread); if (!smtp_hash_get_type(entry, &action)) { /*LOG This message indicates that the policy type is invalid for the given response and Zorp aborts the connection. Check the 'response' attribute. */ z_proxy_log(self, SMTP_POLICY, 1, "Invalid response policy; request='%s', response='%s'", self->request->str, self->response->str); z_proxy_return(self, SMTP_RSP_ABORT); } z_policy_unlock(self->super.thread); switch (action) { case SMTP_RSP_REJECT: z_policy_lock(self->super.thread); if (!z_policy_var_parse_tuple(entry, "i|ss", &action, &response, &response_param)) { /*LOG This message indicates that the parameter of the response policy of the given request is invalid and Zorp aborts the connection. Check the 'response' attribute. */ z_proxy_log(self, SMTP_POLICY, 1, "Error in response policy; request='%s', response='%s'", self->request->str, self->response->str); action = SMTP_RSP_ABORT; } else { if (response) g_string_assign(self->error_code, response); if (response_param) g_string_assign(self->error_info, response_param); } z_policy_unlock(self->super.thread); break; case SMTP_RSP_ACCEPT: case SMTP_RSP_ABORT: break; case SMTP_RSP_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(entry, "(iO)", &action, &process_rsp)) { /*LOG This message indicates that the parameter of the response policy of the given request is invalid and Zorp aborts the connection. Check the 'response' attribute. */ z_proxy_log(self, SMTP_POLICY, 1, "Error in response policy; request='%s', response='%s'", self->request->str, self->response->str); action = SMTP_RSP_ABORT; } else { res = z_policy_call_object(process_rsp, z_policy_var_build("(ssss)", self->request->str, self->request_param->str, self->response->str, self->response_param->str), self->super.session_id); if (res) { if (!z_policy_var_parse(res, "i", &action)) { /*LOG This message indicates that the returned value of the callback for the given response policy is invalid and Zorp aborts the connection. Check the callback function. */ z_proxy_log(self, SMTP_POLICY, 1, "The verdict returned by the policy is not an int; request='%s', response='%s'", self->request->str, self->response->str); action = SMTP_RSP_ABORT; } } else { action = SMTP_RSP_ABORT; } } z_policy_unlock(self->super.thread); break; default: action = SMTP_RSP_ABORT; break; } z_proxy_return(self, action); } gboolean smtp_policy_is_extension_permitted(SmtpProxy *self, gchar *extension) { ZPolicyObj *e; SmtpExtensionDesc *ed; SmtpActionTypes verdict = SMTP_EXT_DROP; gboolean found; z_proxy_enter(self); /* compatibility, check permit_extensions first */ ed = g_hash_table_lookup(known_extensions, extension); if (ed && (self->permit_extensions & ed->extension_mask)) z_proxy_return(self, TRUE); e = g_hash_table_lookup(self->extensions, extension); if (!e) e = g_hash_table_lookup(self->extensions, "*"); if (!e) z_proxy_return(self, FALSE); z_policy_lock(self->super.thread); found = smtp_hash_get_type(e, &verdict); z_policy_unlock(self->super.thread); z_proxy_return(self, found && (verdict == SMTP_EXT_ACCEPT)); } ZPolicyObj * smtp_policy_sanitize_address(SmtpProxy *self, ZPolicyObj *args) { gchar *address; gchar *final_end; GString *sanitized_address; ZPolicyObj *res = NULL; z_proxy_enter(self); if (!z_policy_var_parse_tuple(args, "s", &address)) { z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid arguments"); z_proxy_leave(self); return NULL; } sanitized_address = g_string_new(""); if (!smtp_sanitize_address(self, sanitized_address, address, TRUE, &final_end)) { z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid address"); goto exit; } res = z_policy_var_build("s", sanitized_address->str); exit: g_string_free(sanitized_address, TRUE); z_proxy_leave(self); return res; } zorp-3.9.5/modules/smtp/tests/000077500000000000000000000000001172670260400163245ustar00rootroot00000000000000zorp-3.9.5/modules/smtp/tests/Makefile.am000066400000000000000000000004141172670260400203570ustar00rootroot00000000000000LIBS=@MODULETESTS_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ check_PROGRAMS = smtp_sanitize_addr smtp_sanitize_addr_SOURCES = smtp_sanitize_addr.c smtp_sanitize_addr_LDADD = ../smtpcmd.lo ../smtp.lo ../smtppolicy.lo ../smtpdata.lo ../smtpmsg.lo TESTS = smtp_sanitize_addr zorp-3.9.5/modules/smtp/tests/smtp_sanitize_addr.c000066400000000000000000000061271172670260400223610ustar00rootroot00000000000000#include "../smtp.h" SmtpProxy *dummy; int exit_code = 0; #define SAFE_STR(x) ((x) ? x : "(NULL)") void test_case(gchar *path, gchar *email, gboolean expected) { GString *result = g_string_sized_new(128); gchar *end = 0; if (smtp_sanitize_address(dummy, result, path, TRUE, &end) == expected) { /* printf("result->str=%s\n", result->str); */ /* for parser debugging */ if (expected && strcmp(result->str, email) == 0) printf("success, path=%s, email=%s, end=%s\n", path, email, end); else if (!expected) printf("success, path=%s, end=%s\n", path, end); else { printf("failure, different email, path=%s, email=%s, end=%s\n", path, email, end); exit_code = 1; } } else { printf("failure, different parsing, path=%s, email=%s, result=%s, end=%s\n", path, email, result->str, end); exit_code = 1; } g_string_free(result, TRUE); } int main() { dummy = (SmtpProxy *) z_object_new(Z_CLASS(SmtpProxy)); dummy->append_domain = g_string_sized_new(0); /* z_charset_parse(&dummy.local_part, "a-zA-Z0-9\\-=._"); */ test_case("", "bazsi@balabit.hu", TRUE); test_case("<>", "", TRUE); test_case("<@hop1,@hop2,@hop3:bazsi@balabit.hu>", "bazsi@balabit.hu", TRUE); test_case("<@:bazsi@balabit.hu>", NULL, FALSE); test_case("<:bazsi@balabit.hu>", NULL, FALSE); test_case("<@hop1@bazsi@balabit.hu>", NULL, FALSE); test_case("<@hop1\"bazsi@balabit.hu>", NULL, FALSE); test_case("<@hop1:;bazsi@balabit.hu>", NULL, FALSE); test_case("<@hop1,bazsi@balabit.hu>", NULL, FALSE); test_case("", NULL, FALSE); test_case("bazsi@balabit.hu", NULL, FALSE); test_case("", NULL, FALSE); test_case("<\"balazs scheidler\"@balabit.hu>", "\"balazs scheidler\"@balabit.hu", TRUE); test_case("<\"balazs scheidler\"@[1.2.3.4]>", "\"balazs scheidler\"@[1.2.3.4]", TRUE); test_case("<@hop1.domain,@hop2.domain:\"balazs scheidler\"@[1.2.3.4]>", "\"balazs scheidler\"@[1.2.3.4]", TRUE); test_case("<@hop1.domain,@hop2.domain:\"balazs scheidler\"@[domain literal]>", "\"balazs scheidler\"@[domain literal]", TRUE); test_case("<@hop1.domain,@hop2.domain:\"balazs scheidler\"@#123456>", "\"balazs scheidler\"@#123456", TRUE); test_case("<@hop1.domain,@hop2.domain:\"balazs scheidler\"@#123456z>", NULL, FALSE); test_case(" SIZE=10037", "bounce-debian-gcc=asd=balabit.hu@lists.debian.org", TRUE); test_case("bazsi@balabit.hu", NULL, FALSE); test_case("", NULL, FALSE); dummy->permit_omission_of_angle_brackets = TRUE; printf("------\n"); test_case("bazsi@balabit.hu", "bazsi@balabit.hu", TRUE); test_case("", "bazsi@balabit.hu", TRUE); test_case("", "bazsi@balabit.hu", FALSE); test_case("bounce-debian-gcc=asd=balabit.hu@lists.debian.org SIZE=10037", "bounce-debian-gcc=asd=balabit.hu@lists.debian.org", TRUE); return exit_code; } zorp-3.9.5/modules/telnet/000077500000000000000000000000001172670260400154725ustar00rootroot00000000000000zorp-3.9.5/modules/telnet/Makefile.am000066400000000000000000000004671172670260400175350ustar00rootroot00000000000000pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Telnet.py pkglib_LTLIBRARIES = libtelnet.la libtelnet_la_SOURCES = telnet.c telnetpolicy.c telnetoption.c telnet.h telnetpolicy.h telnetoption.h EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/telnet/Telnet.py000066400000000000000000001344351172670260400173110ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : hidden ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Proxy for the Telnet protocol. The Telnet module defines the classes constituting the proxy for the TELNET protocol.
The Telnet protocol The Telnet protocol was designed to remotely login to computers via the network. Although its main purpose is to access a remote standard terminal, it can be used for many other functions as well. The protocol follows a simple scenario. The client opens a TCP connection to the server at the port 23. The server authenticates the client and opens a terminal. At the end of the session the server closes the connection. All data is sent in plain text format whithout any encryption.
The network virtual terminal The communication is based on the network virtual terminal (NVT). Its goal is to map a character terminal so neither the "server" nor "user" hosts need to keep information about the characteristics of each other's terminals and terminal handling conventions. NVT uses 7 bit code ASCII characters as the display device. An end of line is transmitted as a CRLF (carriage return followed by a line feed). NVT ASCII is used by many other protocols as well. NVT defines three mandatory control codes which must be understood by the participants: NULL, CR (Carriage Return), which moves the printer to the left margin of the current line and LF (Line Feed), which moves the printer to the next line keeping the current horizontal position. NVT also contains some optional commands which are useful. These are the following: BELL is an audible or visual sign. BS (Back Space) moves the printer back one position and deletes a character. HT (Horizontal Tab) moves the printer to the next horizontal tabular stop. VT Vertical Tab moves the printer to the next vertical tabular stop. FF (Form Feed) moves the printer to the top of the next page.
Protocol elements The protocol uses several commands that control the method and various details of the interaction between the client and the server. These commands can be either mandatory commands or extensions. During the session initialization the client and the server negotiates the connection parameters with these commands. Sub-negotiation is a process during the protocol which is for exchanging extra parameters of a command (e.g.: sending the window size). The commands of the protocol are: Telnet protocol commands Request/Response Description SE End of sub-negotiation parameters. NOP No operation. DM Data mark - Indicates the position of Sync event within the data stream. BRK Break - Indicates that a break or attention key was hit. IP Suspend, interrupt or abort the process. AO Abort output - Run a command without sending the output back to the client. AYT Are you there - Request a visible evidence that the AYT command has been received. EC Erase character - Delete the character last received from the stream. EL Erase line - Erase a line without a CRLF. GA Go Ahead - Instruct the other machine to start the transmission. SB Sub-negotiation starts here. WILL Will (option code) - Indicates the desire to begin performing the indicated option, or confirms that it is being performed. WONT Will not (option code) - Indicates the refusal to perform, or continue performing, the indicated option. DO Do (option code) - Indicates the request that the other party perform, or confirmation that the other party is expected to perform, the indicated option. DONT Do not (option code) - Indicates the request that the other party stop performing the indicated option, or confirmation that its performing is no longer expected. IAC Interpret as command.
Proxy behavior TelnetProxy is a module built for parsing TELNET protocol commands and the negotiation process. It reads and parses COMMANDs on the client side, and sends them to the server if the local security policy permits. Arriving RESPONSEs are parsed as well and sent to the client if the local security policy permits. It is possible to manipulate options by using TELNET_OPT_POLICY. It is also possible to accept or deny certain options and suboptions. The Telnet shell itself cannot be controlled, thus the commands issued by the users cannot be monitored or modified.
Default policy The low level abstract Telnet proxy denies every option and suboption negotiation sequences by default. The different options can be enabled either manually in a derived proxy class, or the predefined TelnetProxy class can be used.
Configuring policies for the TELNET protocol The Telnet proxy can enable/disable the use of the options and their suboptions within the session. Changing the default policy can be done using the option multi-dimensional hash, indexed by the option and the suboption (optional). If the suboption is specified, the lookup precedence described in is used. The possible action codes are listed in the table below. Example for disabling the Telnet X Display Location option class MyTelnetProxy(TelnetProxy): def config(self): TelnetProxy.config(self) self.option[TELNET_X_DISPLAY_LOCATION] = (TELNET_OPT_REJECT) Constants have been defined for the easier use of TELNET options and suboptions. These are listed in .
Policy callback functions Policy callback functions can be used to make decisions based on the content of the suboption negotiation sequence. For example, the suboption negotiation sequences of the Telnet Environment option transfer environment variables. The low level proxy implementation parses these variables, and passes their name and value to the callback function one-by-one. These values can also be manipulated during transfer, by changing the current_var_name and current_var_value attributes of the proxy class. Rewriting the DISPLAY environment variable class MyRewritingTelnetProxy(TelnetProxy): def config(self): TelnetProxy.config() self.option[TELNET_ENVIRONMENT, TELNET_SB_IS] = (TELNET_OPTION_POLICY, self.rewriteVar) def rewriteVar(self, option, name, value): if name == "DISPLAY": self.current_var_value = "rewritten_value:0" return TELNET_OPTION_ACCEPT
Option negotiation In the Telnet protocol, options and the actual commands are represented on one byte. In order to be able to use a command in a session, the option (and its suboptions if there are any) corresponding to the command has to be negotiated between the client and the server. Usually the command and the option is represented by the same value, e.g.: the TELNET_STATUS command and option are both represented by the value "5". However, this is not always the case. The negotiation hash is indexed by the code of the command, and contains the code of the option to be negotiated for the given command (or the TELNET_NEG_NONE when no negotation is needed). Currently the only command where the code of the command differs from the related option is self.negotiation["239"] = int(TELNET_EOR).
Related standards The Telnet protocol is described in RFC 854. The different options of the protocol are described in various other RFCs, listed in .
TELNET appendix The constants defined for the easier use of TELNET options and suboptions are listed in the table below. Suboptions are listed directly under the option they refer to. All suboptions have the TELNET_SB prefix. The RFC describing the given option is also shown in the table. TELNET options and suboptions NameConstant value of option/suboptionDetailed in RFC #TELNET_BINARY0856TELNET_ECHO1857TELNET_SUPPRESS_GO_AHEAD3858TELNET_STATUS5859 TELNET_SB_STATUS_SB_IS0TELNET_SB_STATUS_SB_SEND1TELNET_TIMING_MARK6860TELNET_RCTE7726TELNET_NAOCRD10652TELNET_SB_NAOCRD_DR0TELNET_SB_NAOCRD_DS1TELNET_NAOHTS11653TELNET_SB_NAOHTS_DR0TELNET_SB_NAOHTS_DS1TELNET_NAOHTD12654TELNET_SB_NAOHTD_DR0TELNET_SB_NAOHTD_DS1TELNET_NAOFFD13655TELNET_SB_NAOFFD_DR0TELNET_SB_NAOFFD_DS1TELNET_NAOVTS14656TELNET_SB_NAOVTS_DR0TELNET_SB_NAOVTS_DS1TELNET_NAOVTD15657TELNET_SB_NAOVTD_DR0TELNET_SB_NAOVTD_DS1TELNET_NAOLFD16658TELNET_SB_NAOLFD_DR0TELNET_SB_NAOLFD_DS1TELNET_EXTEND_ASCII17698TELNET_LOGOUT18727TELNET_BM19735TELNET_SB_BM_DEFINE1TELNET_SB_BM_ACCEPT2TELNET_SB_BM_REFUSE3TELNET_SB_BM_LITERAL4TELNET_SB_BM_CANCEL5TELNET_DET201043, 732TELNET_SB_DET_DEFINE1TELNET_SB_DET_ERASE2TELNET_SB_DET_TRANSMIT3TELNET_SB_DET_FORMAT4TELNET_SB_DET_MOVE_CURSOR5TELNET_SB_DET_SKIP_TO_LINE6TELNET_SB_DET_SKIP_TO_CHAR7TELNET_SB_DET_UP8TELNET_SB_DET_DOWN9TELNET_SB_DET_LEFT10TELNET_SB_DET_RIGHT11TELNET_SB_DET_HOME12TELNET_SB_DET_LINE_INSERT13TELNET_SB_DET_LINE_DELETE14TELNET_SB_DET_CHAR_INSERT15TELNET_SB_DET_CHAR_DELETE16TELNET_SB_DET_READ_CURSOR17TELNET_SB_DET_CURSOR_POSITION18TELNET_SB_DET_REVERSE_TAB19TELNET_SB_DET_TRANSMIT_SCREEN20TELNET_SB_DET_TRANSMIT_UNPROTECTED21TELNET_SB_DET_TRANSMIT_LINE22TELNET_SB_DET_TRANSMIT_FIELD23TELNET_SB_DET_TRANSMIT_REST_SCREEN24TELNET_SB_DET_TRANSMIT_REST_LINE25TELNET_SB_DET_TRANSMIT_REST_FIELD26TELNET_SB_DET_TRANSMIT_MODIFIED27TELNET_SB_DET_DATA_TRANSMIT28TELNET_SB_DET_ERASE_SCREEN29TELNET_SB_DET_ERASE_LINE30TELNET_SB_DET_ERASE_FIELD31TELNET_SB_DET_ERASE_REST_SCREEN32TELNET_SB_DET_ERASE_REST_LINE33TELNET_SB_DET_ERASE_REST_FIELD34TELNET_SB_DET_ERASE_UNPROTECTED35TELNET_SB_DET_FORMAT_DATA36TELNET_SB_DET_REPEAT37TELNET_SB_DET_SUPPRESS_PROTECTION38TELNET_SB_DET_FIELD_SEPARATOR39TELNET_SB_DET_FN40TELNET_SB_DET_ERROR41TELNET_SUPDUP21736, 734TELNET_SUPDUP_OUTPUT22749TELNET_SEND_LOCATION23779TELNET_TERMINAL_TYPE241091TELNET_SB_TERMINAL_TYPE_IS0TELNET_SB_TERMINAL_TYPE_SEND1TELNET_EOR25885TELNET_TUID26927TELNET_OUTMRK27933TELNET_TTYLOC28 946TELNET_3270_REGIME291041TELNET_SB_3270_REGIME_IS0TELNET_SB_3270_REGIME_ARE1TELNET_X3_PAD301053TELNET_SB_X3_PAD_SET0TELNET_SB_X3_PAD_RESPONSE_SET1TELNET_SB_X3_PAD_IS2TELNET_SB_X3_PAD_RESPONSE_IS3TELNET_SB_X3_PAD_SEND4TELNET_NAWS311073TELNET_TERMINAL_SPEED321079TELNET_SB_TERMINAL_SPEED_IS0TELNET_SB_TERMINAL_SPEED_SEND1TELNET_TOGGLE_FLOW_CONTROL331372TELNET_SB_TOGGLE_FLOW_CONTROL_OFF0TELNET_SB_TOGGLE_FLOW_CONTROL_ON1TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_ANY 2TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_XON 3TELNET_LINEMODE341184TELNET_SB_LINEMODE_MODE1TELNET_SB_LINEMODE_FORWARDMASK2TELNET_SB_LINEMODE_SLC3TELNET_X_DISPLAY_LOCATION351096TELNET_SB_X_DISPLAY_LOCATION_IS0TELNET_SB_X_DISPLAY_LOCATION_SEND1TELNET_OLD_ENVIRONMENT361408TELNET_SB_OLD_ENVIRONMENT_IS0TELNET_SB_OLD_ENVIRONMENT_SEND1TELNET_SB_OLD_ENVIRONMENT_INFO2TELNET_AUTHENTICATION372941TELNET_SB_AUTHENTICATION_IS0TELNET_SB_AUTHENTICATION_SEND1TELNET_SB_AUTHENTICATION_REPLY2TELNET_SB_AUTHENTICATION_NAME3TELNET_ENCRYPT382946TELNET_SB_ENCRYPT_IS0TELNET_SB_ENCRYPT_SUPPORT1TELNET_SB_ENCRYPT_REPLY2TELNET_SB_ENCRYPT_START3TELNET_SB_ENCRYPT_END4TELNET_SB_ENCRYPT_REQUEST_START5TELNET_SB_ENCRYPT_REQUEST_END6TELNET_SB_ENCRYPT_ENC_KEYID7TELNET_SB_ENCRYPT_DEC_KEYID8TELNET_ENVIRONMENT391572TELNET_SB_ENVIRONMENT_IS0TELNET_SB_ENVIRONMENT_SEND1TELNET_SB_ENVIRONMENT_INFO2TELNET_TN3270E401647TELNET_SB_TN3270E_ASSOCIATE0TELNET_SB_TN3270E_CONNECT1TELNET_SB_TN3270E_DEVICE_TYPE2TELNET_SB_TN3270E_FUNCTIONS3TELNET_SB_TN3270E_IS4TELNET_SB_TN3270E_REASON5TELNET_SB_TN3270E_REJECT6TELNET_SB_TN3270E_REQUEST7TELNET_SB_TN3270E_SEND8TELNET_CHARSET422066TELNET_SB_CHARSET_REQUEST1TELNET_SB_CHARSET_ACCEPTED2TELNET_SB_CHARSET_REJECTED3TELNET_SB_CHARSET_TTABLE_IS4TELNET_SB_CHARSET_TTABLE_REJECTED5TELNET_SB_CHARSET_TTABLE_ACK6TELNET_SB_CHARSET_TTABLE_NAK7TELNET_COM_PORT442217TELNET_SB_COM_PORT_CLI_SET_BAUDRATE1TELNET_SB_COM_PORT_CLI_SET_DATASIZE2TELNET_SB_COM_PORT_CLI_SET_PARITY3TELNET_SB_COM_PORT_CLI_SET_STOPSIZE4TELNET_SB_COM_PORT_CLI_SET_CONTROL5TELNET_SB_COM_PORT_CLI_NOTIFY_LINESTATE6TELNET_SB_COM_PORT_CLI_NOTIFY_MODEMSTATE 7TELNET_SB_COM_PORT_CLI_FLOWCONTROL_SUSPEND 8TELNET_SB_COM_PORT_CLI_FLOWCONTROL_RESUME 9TELNET_SB_COM_PORT_CLI_SET_LINESTATE_MASK 10TELNET_SB_COM_PORT_CLI_SET_MODEMSTATE_MASK 11TELNET_SB_COM_PORT_CLI_PURGE_DATA12TELNET_SB_COM_PORT_SVR_SET_BAUDRATE101TELNET_SB_COM_PORT_SVR_SET_DATASIZE102TELNET_SB_COM_PORT_SVR_SET_PARITY103TELNET_SB_COM_PORT_SVR_SET_STOPSIZE104TELNET_SB_COM_PORT_SVR_SET_CONTROL105TELNET_SB_COM_PORT_SVR_NOTIFY_LINESTATE106TELNET_SB_COM_PORT_SVR_NOTIFY_MODEMSTATE 107TELNET_SB_COM_PORT_SVR_FLOWCONTROL_SUSPEND 108TELNET_SB_COM_PORT_SVR_FLOWCONTROL_RESUME 109TELNET_SB_COM_PORT_SVR_SET_LINESTATE_MASK 110TELNET_SB_COM_PORT_SVR_SET_MODEMSTATE_MASK 111TELNET_SB_COM_PORT_SVR_PURGE_DATA112TELNET_KERMIT472840TELNET_SB_KERMIT_START_SERVER0TELNET_SB_KERMIT_STOP_SERVER1TELNET_SB_KERMIT_REQ_START_SERVER2TELNET_SB_KERMIT_REQ_STOP_SERVER3TELNET_SB_KERMIT_SOP4TELNET_SB_KERMIT_RESP_START_SERVER8TELNET_SB_KERMIT_RESP_STOP_SERVER9TELNET_EXOPL255861TELNET_SUBLIMINAL_MSG2571097
Telnet proxy option hashes. TELNET_OPT_ACCEPT TELNET_OPT_REJECT TELNET_OPT_ABORT TELNET_OPT_DROP TELNET_OPT_POLICY Protocol parameter hashes to control the dataflow. TELNET_TERMINAL_TYPE TELNET_TERMINAL_SPEED TELNET_X_DISPLAY_LOCATION TELNET_ENVIRONMENT TELNET_SUPPRESS_GO_AHEAD TELNET_ECHO TELNET_NAWS SB hashes of Telnet. TELNET_SB_IS TELNET_SB_SEND TELNET_SB_INFO Parameters of the logging system. These values are printed into the log messages. TELNET_POLICY "telnet.policy" Action codes for Telnet options Allow the option. Reject the option. Reject the option and terminate the Telnet session. METHOD Call the function specified to make a decision about the event. The function receives two parameters: self, and option (an integer). See for details.
""" from Zorp import * from Proxy import Proxy TELNET_OPT_ACCEPT = 1 TELNET_OPT_REJECT = 3 TELNET_OPT_ABORT = 4 TELNET_OPT_DROP = 5 TELNET_OPT_POLICY = 6 # No negotiation is needed for that command TELNET_NEG_NONE = 255 # option name value RFC, subopt # ------------------------- ------ ----------- #TELNET_BINARY = "0" # 856 TELNET_ECHO = "1" # 857 TELNET_SUPPRESS_GO_AHEAD = "3" # 858 #TELNET_STATUS = "5" # 859 #TELNET_SB_STATUS_SB_IS = "0" #TELNET_SB_STATUS_SB_SEND = "1" #TELNET_TIMING_MARK = "6" # 860 #TELNET_RCTE = "7" # 726 #TELNET_NAOCRD = "10" # 652 #TELNET_SB_NAOCRD_DR = "0" #TELNET_SB_NAOCRD_DS = "1" #TELNET_NAOHTS = "11" # 653 #TELNET_SB_NAOHTS_DR = "0" #TELNET_SB_NAOHTS_DS = "1" #TELNET_NAOHTD = "12" # 654 #TELNET_SB_NAOHTD_DR = "0" #TELNET_SB_NAOHTD_DS = "1" #TELNET_NAOFFD = "13" # 655 #TELNET_SB_NAOFFD_DR = "0" #TELNET_SB_NAOFFD_DS = "1" #TELNET_NAOVTS = "14" # 656 #TELNET_SB_NAOVTS_DR = "0" #TELNET_SB_NAOVTS_DS = "1" #TELNET_NAOVTD = "15" # 657 #TELNET_SB_NAOVTD_DR = "0" #TELNET_SB_NAOVTD_DS = "1" #TELNET_NAOLFD = "16" # 658 #TELNET_SB_NAOLFD_DR = "0" #TELNET_SB_NAOLFD_DS = "1" #TELNET_EXTEND_ASCII = "17" # 698 #TELNET_LOGOUT = "18" # 727 #TELNET_BM = "19" # 735 #TELNET_SB_BM_DEFINE = "1" #TELNET_SB_BM_ACCEPT = "2" #TELNET_SB_BM_REFUSE = "3" #TELNET_SB_BM_LITERAL = "4" #TELNET_SB_BM_CANCEL = "5" #TELNET_DET = "20" # 1043, 732 #TELNET_SB_DET_DEFINE = "1" #TELNET_SB_DET_ERASE = "2" #TELNET_SB_DET_TRANSMIT = "3" #TELNET_SB_DET_FORMAT = "4" #TELNET_SB_DET_MOVE_CURSOR = "5" #TELNET_SB_DET_SKIP_TO_LINE = "6" #TELNET_SB_DET_SKIP_TO_CHAR = "7" #TELNET_SB_DET_UP = "8" #TELNET_SB_DET_DOWN = "9" #TELNET_SB_DET_LEFT = "10" #TELNET_SB_DET_RIGHT = "11" #TELNET_SB_DET_HOME = "12" #TELNET_SB_DET_LINE_INSERT = "13" #TELNET_SB_DET_LINE_DELETE = "14" #TELNET_SB_DET_CHAR_INSERT = "15" #TELNET_SB_DET_CHAR_DELETE = "16" #TELNET_SB_DET_READ_CURSOR = "17" #TELNET_SB_DET_CURSOR_POSITION = "18" #TELNET_SB_DET_REVERSE_TAB = "19" #TELNET_SB_DET_TRANSMIT_SCREEN = "20" #TELNET_SB_DET_TRANSMIT_UNPROTECTED = "21" #TELNET_SB_DET_TRANSMIT_LINE = "22" #TELNET_SB_DET_TRANSMIT_FIELD = "23" #TELNET_SB_DET_TRANSMIT_REST_SCREEN = "24" #TELNET_SB_DET_TRANSMIT_REST_LINE = "25" #TELNET_SB_DET_TRANSMIT_REST_FIELD = "26" #TELNET_SB_DET_TRANSMIT_MODIFIED = "27" #TELNET_SB_DET_DATA_TRANSMIT = "28" #TELNET_SB_DET_ERASE_SCREEN = "29" #TELNET_SB_DET_ERASE_LINE = "30" #TELNET_SB_DET_ERASE_FIELD = "31" #TELNET_SB_DET_ERASE_REST_SCREEN = "32" #TELNET_SB_DET_ERASE_REST_LINE = "33" #TELNET_SB_DET_ERASE_REST_FIELD = "34" #TELNET_SB_DET_ERASE_UNPROTECTED = "35" #TELNET_SB_DET_FORMAT_DATA = "36" #TELNET_SB_DET_REPEAT = "37" #TELNET_SB_DET_SUPPRESS_PROTECTION = "38" #TELNET_SB_DET_FIELD_SEPARATOR = "39" #TELNET_SB_DET_FN = "40" #TELNET_SB_DET_ERROR = "41" #TELNET_SUPDUP = "21" # 736, 734 #TELNET_SUPDUP_OUTPUT = "22" # 749 #TELNET_SEND_LOCATION = "23" # 779 TELNET_TERMINAL_TYPE = "24" # 1091 TELNET_SB_TERMINAL_TYPE_IS = "0" TELNET_SB_TERMINAL_TYPE_SEND = "1" TELNET_EOR = "25" # 885 #TELNET_TUID = "26" # 927 #TELNET_OUTMRK = "27" # 933 #TELNET_TTYLOC = "28 # 946 #TELNET_3270_REGIME = "29" # 1041 #TELNET_SB_3270_REGIME_IS = "0" #TELNET_SB_3270_REGIME_ARE = "1" #TELNET_X3_PAD = "30" # 1053 #TELNET_SB_X3_PAD_SET = "0" #TELNET_SB_X3_PAD_RESPONSE_SET = "1" #TELNET_SB_X3_PAD_IS = "2" #TELNET_SB_X3_PAD_RESPONSE_IS = "3" #TELNET_SB_X3_PAD_SEND = "4" TELNET_NAWS = "31" # 1073 TELNET_TERMINAL_SPEED = "32" # 1079 TELNET_SB_TERMINAL_SPEED_IS = "0" TELNET_SB_TERMINAL_SPEED_SEND = "1" #TELNET_TOGGLE_FLOW_CONTROL = "33" # 1372 #TELNET_SB_TOGGLE_FLOW_CONTROL_OFF = "0" #TELNET_SB_TOGGLE_FLOW_CONTROL_ON = "1" #TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_ANY = "2" #TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_XON = "3" #TELNET_LINEMODE = "34" # 1184 #TELNET_SB_LINEMODE_MODE = "1" #TELNET_SB_LINEMODE_FORWARDMASK = "2" #TELNET_SB_LINEMODE_SLC = "3" TELNET_X_DISPLAY_LOCATION = "35" # 1096 TELNET_SB_X_DISPLAY_LOCATION_IS = "0" TELNET_SB_X_DISPLAY_LOCATION_SEND = "1" #TELNET_OLD_ENVIRONMENT = "36" # 1408 #TELNET_SB_OLD_ENVIRONMENT_IS = "0" #TELNET_SB_OLD_ENVIRONMENT_SEND = "1" #TELNET_SB_OLD_ENVIRONMENT_INFO = "2" #TELNET_AUTHENTICATION = "37" # 2941 #TELNET_SB_AUTHENTICATION_IS = "0" #TELNET_SB_AUTHENTICATION_SEND = "1" #TELNET_SB_AUTHENTICATION_REPLY = "2" #TELNET_SB_AUTHENTICATION_NAME = "3" #TELNET_ENCRYPT = "38" # 2946 #TELNET_SB_ENCRYPT_IS = "0" #TELNET_SB_ENCRYPT_SUPPORT = "1" #TELNET_SB_ENCRYPT_REPLY = "2" #TELNET_SB_ENCRYPT_START = "3" #TELNET_SB_ENCRYPT_END = "4" #TELNET_SB_ENCRYPT_REQUEST_START = "5" #TELNET_SB_ENCRYPT_REQUEST_END = "6" #TELNET_SB_ENCRYPT_ENC_KEYID = "7" #TELNET_SB_ENCRYPT_DEC_KEYID = "8" TELNET_ENVIRONMENT = "39" # 1572 TELNET_SB_ENVIRONMENT_IS = "0" TELNET_SB_ENVIRONMENT_SEND = "1" TELNET_SB_ENVIRONMENT_INFO = "2" #TELNET_TN3270E = "40" # 1647 #TELNET_SB_TN3270E_ASSOCIATE = "0" #TELNET_SB_TN3270E_CONNECT = "1" #TELNET_SB_TN3270E_DEVICE_TYPE = "2" #TELNET_SB_TN3270E_FUNCTIONS = "3" #TELNET_SB_TN3270E_IS = "4" #TELNET_SB_TN3270E_REASON = "5" #TELNET_SB_TN3270E_REJECT = "6" #TELNET_SB_TN3270E_REQUEST = "7" #TELNET_SB_TN3270E_SEND = "8" #TELNET_CHARSET = "42" # 2066 #TELNET_SB_CHARSET_REQUEST = "1" #TELNET_SB_CHARSET_ACCEPTED = "2" #TELNET_SB_CHARSET_REJECTED = "3" #TELNET_SB_CHARSET_TTABLE_IS = "4" #TELNET_SB_CHARSET_TTABLE_REJECTED = "5" #TELNET_SB_CHARSET_TTABLE_ACK = "6" #TELNET_SB_CHARSET_TTABLE_NAK = "7" #TELNET_COM_PORT = "44" # 2217 #TELNET_SB_COM_PORT_CLI_SET_BAUDRATE = "1" #TELNET_SB_COM_PORT_CLI_SET_DATASIZE = "2" #TELNET_SB_COM_PORT_CLI_SET_PARITY = "3" #TELNET_SB_COM_PORT_CLI_SET_STOPSIZE = "4" #TELNET_SB_COM_PORT_CLI_SET_CONTROL = "5" #TELNET_SB_COM_PORT_CLI_NOTIFY_LINESTATE = "6" #TELNET_SB_COM_PORT_CLI_NOTIFY_MODEMSTATE = "7" #TELNET_SB_COM_PORT_CLI_FLOWCONTROL_SUSPEND = "8" #TELNET_SB_COM_PORT_CLI_FLOWCONTROL_RESUME = "9" #TELNET_SB_COM_PORT_CLI_SET_LINESTATE_MASK = "10" #TELNET_SB_COM_PORT_CLI_SET_MODEMSTATE_MASK = "11" #TELNET_SB_COM_PORT_CLI_PURGE_DATA = "12" #TELNET_SB_COM_PORT_SVR_SET_BAUDRATE = "101" #TELNET_SB_COM_PORT_SVR_SET_DATASIZE = "102" #TELNET_SB_COM_PORT_SVR_SET_PARITY = "103" #TELNET_SB_COM_PORT_SVR_SET_STOPSIZE = "104" #TELNET_SB_COM_PORT_SVR_SET_CONTROL = "105" #TELNET_SB_COM_PORT_SVR_NOTIFY_LINESTATE = "106" #TELNET_SB_COM_PORT_SVR_NOTIFY_MODEMSTATE = "107" #TELNET_SB_COM_PORT_SVR_FLOWCONTROL_SUSPEND = "108" #TELNET_SB_COM_PORT_SVR_FLOWCONTROL_RESUME = "109" #TELNET_SB_COM_PORT_SVR_SET_LINESTATE_MASK = "110" #TELNET_SB_COM_PORT_SVR_SET_MODEMSTATE_MASK = "111" #TELNET_SB_COM_PORT_SVR_PURGE_DATA = "112" #TELNET_KERMIT = "47" # 2840 #TELNET_SB_KERMIT_START_SERVER = "0" #TELNET_SB_KERMIT_STOP_SERVER = "1" #TELNET_SB_KERMIT_REQ_START_SERVER = "2" #TELNET_SB_KERMIT_REQ_STOP_SERVER = "3" #TELNET_SB_KERMIT_SOP = "4" #TELNET_SB_KERMIT_RESP_START_SERVER = "8" #TELNET_SB_KERMIT_RESP_STOP_SERVER = "9" TELNET_EXOPL = "255" # 861 #TELNET_SUBLIMINAL_MSG = "257" # 1097 ################################################################################ # Non-RFC telnet options # # 2 - reconnection # [nic 50005] # 4 - approx msg size negotiation # [ethernet] # 8 - output line width # [nic 50005] # 9 - output page size # [nic 50005] # 41 - xauth # [Earhart] # 43 - remote serial port # [Barnes] # 45 - suppress local echo # [Atmar] # 46 - start tls # [Boe] # 48 - send url # [Croft] # 49 - forward X # [Altman] # 50-137 - unassigned # [IANA] # 138 - telopt pragma logon # [McGregory] # 139 - telopt sspi logon # [McGregory] # 140 - telopt pargma heartbeat # [McGregory] # 141-254 - unassigned # [IANA] # 256 - unassigned # [IANA] TELNET_SB_IS = "0" TELNET_SB_SEND = "1" TELNET_SB_INFO = "2" TELNET_POLICY = "telnet.policy" class AbstractTelnetProxy(Proxy): """ Class encapsulating the abstract Telnet proxy. This class implements the Telnet protocol (as described in RFC 854) and its most common extensions. Although not all possible options are checked by the low level proxy, it is possible to filter any option and suboption negotiation sequences using policy callbacks. AbstractTelnetProxy serves as a starting point for customized proxy classes, but is itself not directly usable. Service definitions should refer to a customized class derived from AbstractTelnetProxy, or one of the predefined TelnetProxy proxy classes. AbstractTelnetProxy denies all options by default. option Normative policy hash for Telnet options indexed by the option and (optionally) the suboption. See also . negotiation Normative hash listing which options must be negotiated for a given command. See for details. timeout 600000 I/O timeout in milliseconds. current_var_name n/a Name of the variable being negotiated. current_var_value n/a Value of the variable being negotiated (e.g.: value of an environment variable, an X display location value, etc.). enable_audit FALSE Enable session auditing. """ name = "telnet" def __init__(self, session): """ Constructor to initialize a TelnetProxy instance. This function initializes a TelnetProxy instance by calling the inherited __init__ constructor with appropriate parameters. """ Proxy.__init__(self, session) def config(self): """ """ pass class TelnetProxy(AbstractTelnetProxy): """ Default Telnet proxy based on AbstractTelnetProxy. TelnetProxy is a proxy class based on AbstractTelnetProxy, allowing the use of all Telnet options. """ def config(self): """ """ self.option["*"] = TELNET_OPT_ACCEPT self.option["*", "*"] = TELNET_OPT_ACCEPT self.negotiation["239"] = int(TELNET_EOR) class TelnetProxyStrict(AbstractTelnetProxy): """ Telnet proxy based on AbstractTelnetProxy, allowing only the minimal command set. TelnetProxyStrict is a proxy class based on AbstractTelnetProxy, allowing the use of the options minimally required for a useful Telnet session. The following options are permitted: ECHO; SUPPRESS_GO_AHEAD; TERMINAL_TYPE; NAWS; EOR; TERMINAL_SPEED; X_DISPLAY_LOCATION; ENVIRONMENT. All other options are rejected. """ def config(self): """ Configuration for TelnetProxy It enables all options needed for a useful Telnet session. """ self.option[TELNET_ECHO] = TELNET_OPT_ACCEPT self.option[TELNET_SUPPRESS_GO_AHEAD] = TELNET_OPT_ACCEPT self.option[TELNET_TERMINAL_TYPE] = TELNET_OPT_ACCEPT self.option[TELNET_NAWS] = TELNET_OPT_ACCEPT self.option[TELNET_EOR] = TELNET_OPT_ACCEPT self.option[TELNET_TERMINAL_SPEED] = TELNET_OPT_ACCEPT self.option[TELNET_X_DISPLAY_LOCATION] = TELNET_OPT_ACCEPT self.option[TELNET_ENVIRONMENT] = TELNET_OPT_ACCEPT self.option["*"] = TELNET_OPT_REJECT self.option[TELNET_TERMINAL_TYPE, TELNET_SB_TERMINAL_TYPE_IS] = TELNET_OPT_ACCEPT self.option[TELNET_TERMINAL_TYPE, TELNET_SB_TERMINAL_TYPE_SEND] = TELNET_OPT_ACCEPT self.option[TELNET_TERMINAL_SPEED, TELNET_SB_TERMINAL_SPEED_IS] = TELNET_OPT_ACCEPT self.option[TELNET_TERMINAL_SPEED, TELNET_SB_TERMINAL_SPEED_SEND] = TELNET_OPT_ACCEPT self.option[TELNET_X_DISPLAY_LOCATION, TELNET_SB_X_DISPLAY_LOCATION_IS] = TELNET_OPT_ACCEPT self.option[TELNET_X_DISPLAY_LOCATION, TELNET_SB_X_DISPLAY_LOCATION_SEND] = TELNET_OPT_ACCEPT self.option[TELNET_ENVIRONMENT, TELNET_SB_ENVIRONMENT_IS] = TELNET_OPT_ACCEPT self.option[TELNET_ENVIRONMENT, TELNET_SB_ENVIRONMENT_SEND] = TELNET_OPT_ACCEPT self.option[TELNET_ENVIRONMENT, TELNET_SB_ENVIRONMENT_INFO] = TELNET_OPT_ACCEPT self.option[TELNET_NAWS, "*"] = TELNET_OPT_ACCEPT self.option[TELNET_EOR, "*"] = TELNET_OPT_ACCEPT self.option["*", "*"] = TELNET_OPT_REJECT self.negotiation["239"] = int(TELNET_EOR) zorp-3.9.5/modules/telnet/telnet.c000066400000000000000000000700561172670260400171410ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Hidden * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "telnet.h" #include "telnetpolicy.h" #include "telnetoption.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static TelnetOptions telnet_options_table[] = { { TELNET_OPTION_TERMINAL_TYPE, telnet_opt_terminal_type }, { TELNET_OPTION_TERMINAL_SPEED, telnet_opt_terminal_speed }, { TELNET_OPTION_X_DISPLAY_LOCATION, telnet_opt_x_display }, { TELNET_OPTION_ENVIRONMENT, telnet_opt_new_env }, { TELNET_OPTION_NAWS, telnet_opt_naws }, { 0, NULL } }; /** * telnet_set_defaults: * @self: * * */ static void telnet_set_defaults(TelnetProxy *self) { int i; z_proxy_enter(self); self->telnet_policy = z_dim_hash_table_new(1, 2, DIMHASH_WILDCARD, DIMHASH_WILDCARD); for (i = 0; i < 256; i++) self->telnet_options[i] = NULL; self->policy_name = g_string_new(""); self->policy_value = g_string_new(""); self->timeout = 600000; self->negotiation = g_hash_table_new(g_str_hash, g_str_equal); z_proxy_return(self); } /** * telnet_register_vars: * @self: * * */ static void telnet_register_vars(TelnetProxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "option", Z_VAR_TYPE_DIMHASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->telnet_policy); z_proxy_var_new(&self->super, "negotiation", Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG, self->negotiation); z_proxy_var_new(&self->super, "current_var_name", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->policy_name); z_proxy_var_new(&self->super, "current_var_value", Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET, self->policy_value); z_proxy_var_new(&self->super, "timeout", Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG, &self->timeout); z_proxy_return(self); } /** * telnet_config_init: * @self: * * */ static void telnet_config_init(TelnetProxy *self) { int i; z_proxy_enter(self); for (i = 0; i < 256; i++) { self->options[i][EP_CLIENT] = 0; self->options[i][EP_SERVER] = 0; } for (i = 0; telnet_options_table[i].option_check != NULL; i++) self->telnet_options[telnet_options_table[i].option] = telnet_options_table[i].option_check; for (i = 0; i < EP_MAX; i++) { self->write_buffers[i].buf = g_new0(guchar, TELNET_BUFFER_SIZE + 1); self->write_buffers[i].size = TELNET_BUFFER_SIZE + 1; self->write_buffers[i].ofs = self->write_buffers[i].end = 0; } z_proxy_return(self); } /** * telnet_stream_read: * @self: * @buf: * @ep: * * * * Returns: * */ static GIOStatus telnet_stream_read(TelnetProxy *self, ZIOBuffer *buf, guint ep) { GIOStatus res; gsize len; z_proxy_enter(self); len = 0; res = z_stream_read(self->super.endpoints[ep], buf->buf + buf->end, sizeof(buf->buf) - buf->end, &len, NULL); buf->end += len; switch (res) { case G_IO_STATUS_NORMAL: z_proxy_return(self, res); case G_IO_STATUS_EOF: z_proxy_return(self, res); case G_IO_STATUS_AGAIN: z_proxy_return(self, res); default: break; } z_proxy_return(self, G_IO_STATUS_ERROR); } /** * telnet_stream_write: * @self: * @buf: * @ep: * * * * Returns: * */ static GIOStatus telnet_stream_write(TelnetProxy *self, ZIOBufferDyn *buf, guint ep) { GIOStatus res; gsize bytes_written; z_proxy_enter(self); if (buf->ofs != buf->end) { res = z_stream_write(self->super.endpoints[ep], &buf->buf[buf->ofs], buf->end - buf->ofs, &bytes_written, NULL); switch (res) { case G_IO_STATUS_NORMAL: buf->ofs += bytes_written; break; case G_IO_STATUS_AGAIN: break; default: z_proxy_return(self, G_IO_STATUS_ERROR); } if (buf->ofs != buf->end) { self->super.endpoints[ep]->want_write = TRUE; z_proxy_return(self, G_IO_STATUS_AGAIN); } } z_proxy_return(self, G_IO_STATUS_NORMAL); } /** * telnet_copy_buf: * @to: * @from: * @bytes: * * * * Returns: * */ static gint telnet_copy_buf(ZIOBufferDyn *to, ZIOBuffer *from, guint bytes) { guint i; z_enter(); if ((i = to->size - to->end) < bytes) { /* we must allocate more buffer space */ to->size += (1 + bytes / TELNET_BUFFER_SIZE) * TELNET_BUFFER_SIZE; to->buf = g_realloc(to->buf, to->size); } for (i = 0; to->end < to->size && from->ofs < from->end && i < bytes; to->end++, from->ofs++, i++) to->buf[to->end] = from->buf[from->ofs]; z_return(i == bytes); } /** * telnet_check_suboption: * @self: * @ep: * * * * Returns: * */ static guint telnet_check_suboption(TelnetProxy *self, guint ep) { guint res; TelnetOptionFunction check_func; ZIOBuffer *sbuf = &self->suboptions[ep]; gchar buf[TELNET_BUFFER_SIZE + 1]; guint i, j; z_proxy_enter(self); /* check if allowed in this session */ if (!(self->options[self->opneg_option[ep]][OTHER_EP(ep)] & (SENT_WILL | GOT_DO)) && !(self->options[self->opneg_option[ep]][ep] & (SENT_WILL | GOT_DO))) { z_proxy_log(self, TELNET_VIOLATION, 3, "Option not allowed in the session; option='%d'", self->opneg_option[ep]); z_proxy_return(self, TELNET_CHECK_ABORT); } /* check if valid */ if ((check_func = self->telnet_options[self->opneg_option[ep]]) == NULL) { /* option has no suboption check function */ /* copy suboption negotiation buffer into policy_value */ for (j = 0, i = sbuf->ofs; i < sbuf->end; j++, i++) buf[j] = sbuf->buf[i]; g_string_assign(self->policy_name, ""); g_string_assign(self->policy_value, buf); /* call policy check */ res = telnet_policy_suboption(self, (guchar) buf[0], "", buf); } else { /* call check function, and check function calls policy */ res = check_func(self, ep); } z_proxy_return(self, res); } /** * telnet_process_opneg: * @self: * @ep: * * * * Returns: * */ static guint telnet_process_opneg(TelnetProxy *self, guint ep) { guint res; z_proxy_enter(self); /* * ask policy if option is enabled */ res = telnet_policy_option(self); if (res == TELNET_CHECK_OK) { switch (self->command[ep]) { case TELNET_CMD_WILL: /* set flag which means this side has sent a WILL */ self->options[self->opneg_option[ep]][ep] |= SENT_WILL; break; case TELNET_CMD_WONT: /* set flag which means this side has sent a WONT */ self->options[self->opneg_option[ep]][ep] &= ~GOT_DO; break; case TELNET_CMD_DO: /* set the other side's flag, indicating that * its WILL was accepted by the other side */ self->options[self->opneg_option[ep]][OTHER_EP(ep)] |= GOT_DO; break; case TELNET_CMD_DONT: /* clear the other side's WILL flag, indicating that * its WILL was refused */ self->options[self->opneg_option[ep]][OTHER_EP(ep)] &= ~SENT_WILL; break; default: z_proxy_log(self, TELNET_VIOLATION, 2, "Unknown command; command='%d'", self->command[ep]); break; } } z_proxy_return(self, res); } /** * telnet_process_command: * @self: * @ep: * * * * Returns: * */ static guint telnet_process_command(TelnetProxy *self, guint ep) { ZPolicyObj *res = NULL; guint option_needed; gchar cmd_str[5]; guint ret_status; z_proxy_enter(self); /* * allow commands defined in RFC 854 * these are important, and must be implemented */ /* NOTE: this triggers a warning in gcc as the second part of the * condition is always TRUE as guchar is always less-or-equal than 255, * this is true, but I leave the condition intact as in the possible case * command is changed to int the condition might be perfectly valid */ if (self->command[ep] >= 240) z_proxy_return(self, TELNET_CHECK_OK); /* * allow negotiated commands * these were allowed during a negotiation */ g_snprintf(cmd_str, sizeof(cmd_str), "%hhu", self->command[ep]); z_policy_lock(self->super.thread); res = g_hash_table_lookup(self->negotiation, cmd_str); if (res != NULL) { if (!z_policy_var_parse(res, "i", &option_needed)) { z_proxy_log(self, TELNET_POLICY, 2, "Value in negotiation table bad; command='%d'", self->command[ep]); z_policy_unlock(self->super.thread); z_proxy_return(self, TELNET_CHECK_REJECT); } z_proxy_trace(self, "Changed needed negotiated option; command='%s', option='%d'", cmd_str, option_needed); } else { option_needed = self->command[ep]; } z_policy_unlock(self->super.thread); ret_status = TELNET_CHECK_REJECT; if (option_needed == 255) { ret_status = TELNET_CHECK_OK; } else if (option_needed > 255) { z_proxy_log(self, TELNET_POLICY, 2, "Value in negotation table out of range; command='%d', value='%d'", self->command[ep], option_needed); } else { z_proxy_trace(self, "Option state check; option='%d', state='%d:%d'", option_needed, self->options[option_needed][ep], self->options[option_needed][OTHER_EP(ep)]); if (self->options[option_needed][ep] & (SENT_WILL | GOT_DO)) ret_status = TELNET_CHECK_OK; } /* reject everything else */ z_proxy_return(self, ret_status); } /** * telnet_process_buf: * @self: * @buf: * @dbuf: * @odbuf: * @ep: * * * * Returns: * */ static gboolean telnet_process_buf(TelnetProxy *self, ZIOBuffer *buf, ZIOBufferDyn *dbuf, ZIOBufferDyn *odbuf, guint ep) { guint ptr; guint res; guchar byte; ZIOBuffer *sbuf = &self->suboptions[ep]; ZIOBuffer tbuf; z_proxy_enter(self); z_proxy_trace(self, "telnet_process_buf called side='%s'", WHICH_EP(ep)); dbuf->ofs = dbuf->end = 0; ptr = buf->ofs; while (ptr < buf->end) { z_proxy_log(self, TELNET_DEBUG, 7, "Processing buffer; state='%d'", self->state[ep]); switch (self->state[ep]) { case TELNET_DATA: while (ptr < buf->end && buf->buf[ptr] != TELNET_IAC) ptr++; /* if not in urgent mode, write out data */ res = telnet_copy_buf(dbuf, buf, ptr - buf->ofs); if (!res) { z_proxy_log(self, TELNET_ERROR, 3, "Output buffer full; side='%s'", WHICH_EP(OTHER_EP(ep))); z_proxy_return(self, FALSE); } else if (ptr >= buf->end) buf->ofs = ptr; /* set buffer offset pointer as if data was written */ if (ptr < buf->end) { self->state[ep] = TELNET_GOT_IAC; ptr++; if (ptr == buf->end) { self->state[ep] = TELNET_DATA; } } break; case TELNET_GOT_IAC: self->command[ep] = buf->buf[ptr++]; /* telnet option negotiation */ if (self->command[ep] == TELNET_CMD_WILL || self->command[ep] == TELNET_CMD_WONT || self->command[ep] == TELNET_CMD_DO || self->command[ep] == TELNET_CMD_DONT) { self->state[ep] = TELNET_GOT_OPNEG; } /* telnet suboption negotiation */ else if (self->command[ep] == TELNET_CMD_SB) { self->state[ep] = TELNET_GOT_SB; } /* telnet datamark */ else if (self->command[ep] == TELNET_CMD_DATAMARK) { self->state[ep] = TELNET_DATA; } /* invalid commands in this state, drop them */ else if (self->command[ep] == TELNET_CMD_SE) { self->state[ep] = TELNET_DATA; z_proxy_log(self, TELNET_VIOLATION, 2, "Illegal command in stream; command='%d'", self->command[ep]); } /* else send it to the other side */ else { res = telnet_process_command(self, ep); self->state[ep] = TELNET_DATA; if (res == TELNET_CHECK_OK) { res = telnet_copy_buf(dbuf, buf, ptr - buf->ofs); if (!res) { z_proxy_log(self, TELNET_ERROR, 3, "Output buffer full; side='%s'", WHICH_EP(OTHER_EP(ep))); z_proxy_return(self, FALSE); } } else { buf->ofs = ptr; z_proxy_log(self, TELNET_VIOLATION, 2, "Illegal command; command='%d'", self->command[ep]); } } z_proxy_log(self, TELNET_DEBUG, 6, "Processing command; state='TELNET_GOT_IAC', cmd='%d'", self->command[ep]); break; case TELNET_GOT_OPNEG: /* get option number from buffer */ self->opneg_option[ep] = buf->buf[ptr++]; z_proxy_log(self, TELNET_DEBUG, 6, "Processing option negotiation; state='TELNET_GOT_OPNEG', option='%d'", self->opneg_option[ep]); /* check if valid and allowed */ res = telnet_process_opneg(self, ep); switch (res) { case TELNET_CHECK_OK: res = telnet_copy_buf(dbuf, buf, ptr - buf->ofs); if (!res) { z_proxy_log(self, TELNET_ERROR, 3, "Output buffer full; side='%s'", WHICH_EP(OTHER_EP(ep))); z_proxy_return(self, FALSE); } break; case TELNET_CHECK_REJECT: /* create a temporary buffer */ tbuf.ofs = 0; tbuf.end = 3; tbuf.buf[0] = buf->buf[buf->ofs]; tbuf.buf[1] = buf->buf[buf->ofs + 1]; tbuf.buf[2] = buf->buf[buf->ofs + 2]; switch (buf->buf[buf->ofs + 1]) { case TELNET_CMD_WILL: tbuf.buf[tbuf.ofs + 1] = TELNET_CMD_DONT; buf->buf[buf->ofs + 1] = TELNET_CMD_WONT; z_proxy_log(self, TELNET_DEBUG, 6, "WILL rejected;"); break; case TELNET_CMD_WONT: tbuf.buf[tbuf.ofs + 1] = TELNET_CMD_DONT; z_proxy_log(self, TELNET_DEBUG, 6, "WONT passed through;"); break; case TELNET_CMD_DO: tbuf.buf[tbuf.ofs + 1] = TELNET_CMD_WONT; buf->buf[buf->ofs + 1] = TELNET_CMD_DONT; z_proxy_log(self, TELNET_DEBUG, 6, "DO rejected;"); break; case TELNET_CMD_DONT: tbuf.buf[tbuf.ofs + 1] = TELNET_CMD_WONT; z_proxy_log(self, TELNET_DEBUG, 6, "DONT passed through;"); break; } res = telnet_copy_buf(odbuf, &tbuf, tbuf.end); if (res) res = telnet_copy_buf(dbuf, buf, ptr - buf->ofs); if (!res) { z_proxy_log(self, TELNET_DEBUG, 6, "Output buffer full; side='%s'", WHICH_EP(ep)); z_proxy_return(self, FALSE); } break; case TELNET_CHECK_ABORT: z_proxy_log(self, TELNET_POLICY, 2, "Session aborted during option negotiation;"); z_proxy_return(self, FALSE); case TELNET_CHECK_DROP: default: z_proxy_log(self, TELNET_POLICY, 3, "Option negotiation sequence dropped;"); break; } /* next state */ self->state[ep] = TELNET_DATA; break; case TELNET_GOT_SB: /* get option number from buffer */ self->opneg_option[ep] = buf->buf[ptr++]; z_proxy_log(self, TELNET_DEBUG, 6, "Processing suboptions; state='TELNET_GOT_SB', option='%d'", self->opneg_option[ep]); /* initialize suboption buffer */ self->suboptions[ep].ofs = 0; self->suboptions[ep].end = 0; self->state[ep] = TELNET_IN_SB; break; case TELNET_IN_SB: /* while not end of buffer and no IAC found */ while (ptr < buf->end && buf->buf[ptr] != TELNET_IAC) { /* if the suboption buffer is already full */ if (sbuf->end == TELNET_SUBOPTION_SIZE) { z_proxy_log(self, TELNET_DEBUG, 6, "Suboption buffer full; side='%s'", WHICH_EP(ep)); z_proxy_return(self, FALSE); } /* copy byte to suboption buffer */ sbuf->buf[sbuf->end++] = buf->buf[ptr++]; } /* if IAC found, next state is TELNET_GOT_SB_IAC */ if (ptr < buf->end) { self->state[ep] = TELNET_GOT_SB_IAC; ptr++; } else { self->state[ep] = TELNET_DATA; } break; case TELNET_GOT_SB_IAC: /* if suboption negotiation end found */ if ((byte = buf->buf[ptr++]) == TELNET_CMD_SE) { res = telnet_check_suboption(self, ep); if (res == TELNET_CHECK_OK) { res = telnet_copy_buf(dbuf, buf, 3); if (res) telnet_copy_buf(dbuf, sbuf, sbuf->end - sbuf->ofs); buf->ofs = ptr - 2; if (res) telnet_copy_buf(dbuf, buf, 2); if (!res) { z_proxy_log(self, TELNET_VIOLATION, 6, "Output buffer full; side='%s'", WHICH_EP(OTHER_EP(ep))); z_proxy_leave(self); return FALSE; } } else { z_proxy_log(self, TELNET_POLICY, 3, "Suboption denied by policy;"); } /* data comes... */ buf->ofs = ptr; self->state[ep] = TELNET_DATA; } /* otherwise it was just suboption data */ else { /* check if there's room for two bytes in suboption buffer */ if (sbuf->end + 2 > TELNET_SUBOPTION_SIZE) { z_proxy_log(self, TELNET_ERROR, 3, "Suboption buffer full; side='%s'", WHICH_EP(ep)); z_proxy_return(self, FALSE); } /* put two bytes in the buffer */ sbuf->buf[sbuf->end++] = TELNET_IAC; sbuf->buf[sbuf->end++] = byte; /* suboption negotiation data follows... */ self->state[ep] = TELNET_IN_SB; } break; default: z_proxy_log(self, TELNET_ERROR, 2, "Internal error, unknown state;"); z_proxy_return(self, FALSE); } } z_proxy_return(self, TRUE); } /** * telnet_forward: * @self: * @from: * @to: * @ep: * * * * Returns: * */ static gboolean telnet_forward(TelnetProxy *self, ZStream *from, ZStream *to, guint ep) { ZIOBuffer *buf = &self->read_buffers[ep]; ZIOBufferDyn *dbuf = &self->write_buffers[OTHER_EP(ep)]; ZIOBufferDyn *odbuf = &self->write_buffers[ep]; guint maxiter = 5; GIOStatus res; gboolean rc; z_proxy_enter(self); from->want_read = FALSE; /* write any pending data in output buffer */ to->want_write = FALSE; res = telnet_stream_write(self, dbuf, OTHER_EP(ep)); if (res != G_IO_STATUS_NORMAL) z_proxy_return(self, res == G_IO_STATUS_AGAIN); /* read and write */ while (maxiter) { maxiter--; if (buf->ofs != buf->end) memmove(buf->buf, buf->buf + buf->ofs, buf->end - buf->ofs); buf->end -= buf->ofs; buf->ofs = 0; res = telnet_stream_read(self, buf, ep); if (res == G_IO_STATUS_NORMAL) { /* process buffer */ rc = telnet_process_buf(self, buf, dbuf, odbuf, ep); if (!rc) z_proxy_return(self, FALSE); /* write output buffer */ if (!from->want_write) { res = telnet_stream_write(self, odbuf, ep); if (res != G_IO_STATUS_NORMAL && res != G_IO_STATUS_AGAIN) z_proxy_return(self, FALSE); } res = telnet_stream_write(self, dbuf, OTHER_EP(ep)); if (res == G_IO_STATUS_AGAIN) break; else if (res != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); } else if (res == G_IO_STATUS_AGAIN) { break; } else if (res == G_IO_STATUS_EOF) { z_proxy_log(self, TELNET_DEBUG, 6, "Connection closed by peer; side='%s'", WHICH_EP(ep)); z_proxy_return(self, FALSE); } else if (res == G_IO_STATUS_ERROR) { z_proxy_return(self, FALSE); } } /* check if output buffer is empty */ if (dbuf->ofs == dbuf->end) from->want_read = TRUE; z_proxy_return(self, TRUE); } /** * telnet_client_read: * @stream: not used * @cond: not used * @user_data: * * * * Returns: * */ static gboolean telnet_client_read(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { TelnetProxy *self = (TelnetProxy *) user_data; gboolean res; z_proxy_enter(self); self->ep = EP_CLIENT; res = telnet_forward(self, self->super.endpoints[EP_CLIENT], self->super.endpoints[EP_SERVER], EP_CLIENT); if (!res) z_poll_quit(self->poll); z_proxy_return(self, res); } /** * telnet_server_read: * @stream: not used * @cond: not used * @user_data: * * * * Returns: * */ static gboolean telnet_server_read(ZStream *stream G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED, gpointer user_data) { TelnetProxy *self = (TelnetProxy *) user_data; gboolean res; z_proxy_enter(self); self->ep = EP_SERVER; res = telnet_forward(self, self->super.endpoints[EP_SERVER], self->super.endpoints[EP_CLIENT], EP_SERVER); if (!res) z_poll_quit(self->poll); z_proxy_return(self, res); } /** * telnet_init_streams: * @self: * * * * Returns: * */ static gboolean telnet_init_streams(TelnetProxy *self) { gboolean ret = TRUE; int fd; int one = 1; z_proxy_enter(self); if (!self->super.endpoints[EP_CLIENT] || !self->super.endpoints[EP_SERVER] || !self->poll) { ret = FALSE; goto exit; } self->read_buffers[EP_SERVER].ofs = self->read_buffers[EP_SERVER].end = 0; self->write_buffers[EP_SERVER].ofs = self->write_buffers[EP_SERVER].end = 0; self->read_buffers[EP_CLIENT].ofs = self->read_buffers[EP_CLIENT].end = 0; self->write_buffers[EP_CLIENT].ofs = self->write_buffers[EP_CLIENT].end = 0; z_stream_set_nonblock(self->super.endpoints[EP_CLIENT], TRUE); z_stream_set_callback(self->super.endpoints[EP_CLIENT], G_IO_IN, telnet_client_read, self, NULL); z_stream_set_callback(self->super.endpoints[EP_CLIENT], G_IO_OUT, telnet_server_read, self, NULL); z_stream_set_cond(self->super.endpoints[EP_CLIENT], G_IO_IN, TRUE); self->super.endpoints[EP_CLIENT]->timeout = -2; fd = z_stream_get_fd (self->super.endpoints[EP_CLIENT]); setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof (one)); z_stream_set_nonblock(self->super.endpoints[EP_SERVER], TRUE); z_stream_set_callback(self->super.endpoints[EP_SERVER], G_IO_IN, telnet_server_read, self, NULL); z_stream_set_callback(self->super.endpoints[EP_SERVER], G_IO_OUT, telnet_client_read, self, NULL); z_stream_set_cond(self->super.endpoints[EP_SERVER], G_IO_IN, TRUE); self->super.endpoints[EP_SERVER]->timeout = -2; fd = z_stream_get_fd (self->super.endpoints[EP_SERVER]); setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof (one)); z_poll_add_stream(self->poll, self->super.endpoints[EP_CLIENT]); z_poll_add_stream(self->poll, self->super.endpoints[EP_SERVER]); exit: z_proxy_return(self, ret); } static void telnet_deinit_streams(TelnetProxy *self) { z_poll_remove_stream(self->poll, self->super.endpoints[EP_SERVER]); z_poll_remove_stream(self->poll, self->super.endpoints[EP_CLIENT]); } /** * telnet_config: * @s: * * * * Returns: * */ static gboolean telnet_config(ZProxy *s) { TelnetProxy *self = Z_CAST(s, TelnetProxy); gboolean success = FALSE; z_proxy_enter(self); self->poll = z_poll_new(); telnet_set_defaults(self); telnet_register_vars(self); if (Z_SUPER(self, ZProxy)->config(s)) { telnet_config_init(self); success = TRUE; } z_proxy_return(self, success); } /** * telnet_main: * @s: * * */ static void telnet_main(ZProxy *s) { TelnetProxy *self = Z_CAST(s, TelnetProxy); z_proxy_enter(self); if (!z_proxy_connect_server(&self->super, NULL, 0) || !telnet_init_streams(self)) { z_proxy_leave(self); return; } self->state[EP_CLIENT] = TELNET_DATA; self->state[EP_SERVER] = TELNET_DATA; while (z_poll_iter_timeout(self->poll, self->timeout)) { if (!z_proxy_loop_iteration(s)) break; } telnet_deinit_streams(self); z_proxy_leave(self); } /** * telnet_proxy_free: * @s: * * */ static void telnet_proxy_free(ZObject *s) { gint i; TelnetProxy *self = Z_CAST(s, TelnetProxy); z_enter(); for (i = 0; i < EP_MAX; i++) g_free(self->write_buffers[i].buf); z_poll_unref(self->poll); self->poll = NULL; z_proxy_free_method(s); z_return(); } /** * telnet_proxy_new: * @params: * * * * Returns: * */ static ZProxy * telnet_proxy_new(ZProxyParams *params) { TelnetProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(TelnetProxy), params), TelnetProxy); z_return((ZProxy *) self); } static void telnet_proxy_free(ZObject *s); ZProxyFuncs telnet_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), telnet_proxy_free, }, .config = telnet_config, .main = telnet_main, }; Z_CLASS_DEF(TelnetProxy, ZProxy, telnet_proxy_funcs); /** * zorp_module_init: * * * * Returns: * */ gint zorp_module_init(void) { z_registry_add("telnet", ZR_PROXY, telnet_proxy_new); return TRUE; } zorp-3.9.5/modules/telnet/telnet.h000066400000000000000000000436211172670260400171440ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Hidden * Auditor: * Last audited version: * Notes: ***************************************************************************/ #ifndef ZORP_MODULES_TELNET_H_INCLUDED #define ZORP_MODULES_TELNET_H_INCLUDED #include #include #include #include /* Telnet command codes */ /* RFC 854 */ #define TELNET_IAC 255 #define TELNET_CMD_SE 240 #define TELNET_CMD_NOP 241 #define TELNET_CMD_DATAMARK 242 #define TELNET_CMD_BRK 243 #define TELNET_CMD_IP 244 #define TELNET_CMD_AO 245 #define TELNET_CMD_AYT 246 #define TELNET_CMD_EC 247 #define TELNET_CMD_EL 248 #define TELNET_CMD_GA 249 #define TELNET_CMD_SB 250 #define TELNET_CMD_WILL 251 #define TELNET_CMD_WONT 252 #define TELNET_CMD_DO 253 #define TELNET_CMD_DONT 254 #define TELNET_CMD_IAC 255 /* Telnet option codes */ /* RFC 856 - TELNET binary transmission */ #define TELNET_OPTION_BINARY 0 /* RFC 857 - TELNET echo */ #define TELNET_OPTION_ECHO 1 /* RFC 858 - TELNET suppress go ahead */ #define TELNET_OPTION_SUPPRESS_GO_AHEAD 3 /* RFC 859 - TELNET status */ #define TELNET_OPTION_STATUS 5 #define TELNET_SB_STATUS_SB_IS 0 #define TELNET_SB_STATUS_SB_SEND 1 /* RFC 860 - TELNET timing mark */ #define TELNET_OPTION_TIMING_MARK 6 /* RFC 726 - TELNET remote controlling transmission and echoing */ #define TELNET_OPTION_RCTE 7 /* suboptions: * IAC SB RCTE [BC1 BC2] [TC1 TC2] IAC SE * contains [BC1 BC2] if (cmd & 8) * contains [TC1 TC2] if (cmd & 16) */ /* RFC 652 - TELNET negotiate about output CR disposition */ #define TELNET_OPTION_NAOCRD 10 #define TELNET_SB_NAOCRD_DR 0 #define TELNET_SB_NAOCRD_DS 1 /* RFC 653 - TELNET negotiate about output horizontal tabstops (HT) */ #define TELNET_OPTION_NAOHTS 11 #define TELNET_SB_NAOHTS_DR 0 #define TELNET_SB_NAOHTS_DS 1 /* RFC 654 - TELNET negotiate about output HT disposition */ #define TELNET_OPTION_NAOHTD 12 #define TELNET_SB_NAOHTD_DR 0 #define TELNET_SB_NAOHTD_DS 1 /* RFC 655 - TELNET negotiate about output FF disposition */ #define TELNET_OPTION_NAOFFD 13 #define TELNET_SB_NAOFFD_DR 0 #define TELNET_SB_NAOFFD_DS 1 /* RFC 656 - TELNET negotiate about vertical tabstops (VT) */ #define TELNET_OPTION_NAOVTS 14 #define TELNET_SB_NAOVTS_DR 0 #define TELNET_SB_NAOVTS_DS 1 /* RFC 657 - TELNET negotiate about VT disposition */ #define TELNET_OPTION_NAOVTD 15 #define TELNET_SB_NAOVTD_DR 0 #define TELNET_SB_NAOVTD_DS 1 /* RFC 658 - TELNET negotiate about LF disposition */ #define TELNET_OPTION_NAOLFD 16 #define TELNET_SB_NAOLFD_DR 0 #define TELNET_SB_NAOLFD_DS 1 /* RFC 698 - TELNET extended ASCII */ #define TELNET_OPTION_EXTEND_ASCII 17 /* suboptions: * IAC SB EXTASC high_byte low_byte IAC SE */ /* RFC 727 - TELNET logout */ #define TELNET_OPTION_LOGOUT 18 /* RFC 735 - TELNET byte macro */ #define TELNET_OPTION_BM 19 #define TELNET_SB_BM_DEFINE 1 #define TELNET_SB_BM_ACCEPT 2 #define TELNET_SB_BM_REFUSE 3 #define TELNET_SB_BM_LITERAL 4 #define TELNET_SB_BM_CANCEL 5 /* suboptions: * IAC SB BM IAC SE * is 1 * may not be IAC (255) * is a 8-bit number, indicating the length of * IAC SB BM IAC SE * is 2 * IAC SB BM IAC SE * is 3 * may be: * is 1 * is 2 * is 3 * is 0 * IAC SB BM IAC SE * is 4 * IAC SB BM IAC SE * is ? */ /* RFC 1043 - TELNET data entry terminal */ #define TELNET_OPTION_DET 20 #define TELNET_SB_DET_DEFINE 1 #define TELNET_SB_DET_ERASE 2 #define TELNET_SB_DET_TRANSMIT 3 #define TELNET_SB_DET_FORMAT 4 #define TELNET_SB_DET_MOVE_CURSOR 5 #define TELNET_SB_DET_SKIP_TO_LINE 6 #define TELNET_SB_DET_SKIP_TO_CHAR 7 #define TELNET_SB_DET_UP 8 #define TELNET_SB_DET_DOWN 9 #define TELNET_SB_DET_LEFT 10 #define TELNET_SB_DET_RIGHT 11 #define TELNET_SB_DET_HOME 12 #define TELNET_SB_DET_LINE_INSERT 13 #define TELNET_SB_DET_LINE_DELETE 14 #define TELNET_SB_DET_CHAR_INSERT 15 #define TELNET_SB_DET_CHAR_DELETE 16 #define TELNET_SB_DET_READ_CURSOR 17 #define TELNET_SB_DET_CURSOR_POSITION 18 #define TELNET_SB_DET_REVERSE_TAB 19 #define TELNET_SB_DET_TRANSMIT_SCREEN 20 #define TELNET_SB_DET_TRANSMIT_UNPROTECTED 21 #define TELNET_SB_DET_TRANSMIT_LINE 22 #define TELNET_SB_DET_TRANSMIT_FIELD 23 #define TELNET_SB_DET_TRANSMIT_REST_SCREEN 24 #define TELNET_SB_DET_TRANSMIT_REST_LINE 25 #define TELNET_SB_DET_TRANSMIT_REST_FIELD 26 #define TELNET_SB_DET_TRANSMIT_MODIFIED 27 #define TELNET_SB_DET_DATA_TRANSMIT 28 #define TELNET_SB_DET_ERASE_SCREEN 29 #define TELNET_SB_DET_ERASE_LINE 30 #define TELNET_SB_DET_ERASE_FIELD 31 #define TELNET_SB_DET_ERASE_REST_SCREEN 32 #define TELNET_SB_DET_ERASE_REST_LINE 33 #define TELNET_SB_DET_ERASE_REST_FIELD 34 #define TELNET_SB_DET_ERASE_UNPROTECTED 35 #define TELNET_SB_DET_FORMAT_DATA 36 #define TELNET_SB_DET_REPEAT 37 #define TELNET_SB_DET_SUPPRESS_PROTECTION 38 #define TELNET_SB_DET_FIELD_SEPARATOR 39 #define TELNET_SB_DET_FN 40 #define TELNET_SB_DET_ERROR 41 /* RFC 736 - TELNET SUPDUP support */ #define TELNET_OPTION_SUPDUP 21 /* RFC 749 - TELNET SUPDUP output */ #define TELNET_OPTION_SUPDUP_OUTPUT 22 /* suboptions: * IAC SB SUPDUP-OUTPUT 1 IAC SE * * IAC SB SUPDUP-OUTPUT 2 n TD1 TD2 .. TDn SCx SCy IAC SE * n is the number of TD bytes */ /* RFC 779 - TELNET SEND-LOCATION */ #define TELNET_OPTION_SEND_LOCATION 23 /* RFC 1091 - TELNET terminal-type */ #define TELNET_OPTION_TERMINAL_TYPE 24 #define TELNET_SB_TERMINAL_TYPE_IS 0 #define TELNET_SB_TERMINAL_TYPE_SEND 1 /* RFC 885 - TELNET end of record */ #define TELNET_OPTION_EOR 25 /* RFC 927 - TELNET TACACS user identification */ #define TELNET_OPTION_TUID 26 /* RFC 933 - TELNET output marking */ #define TELNET_OPTION_OUTMRK 27 /* RFC 946 - TELNET terminal location number */ #define TELNET_OPTION_TTYLOC 28 /* RFC 1041 - TELNET 3270 regime */ #define TELNET_OPTION_3270_REGIME 29 #define TELNET_SB_3270_REGIME_IS 0 #define TELNET_SB_3270_REGIME_ARE 1 /* RFC 1053 - TELNET X.3 PAD */ #define TELNET_OPTION_X3_PAD 30 #define TELNET_SB_X3_PAD_SET 0 #define TELNET_SB_X3_PAD_RESPONSE_SET 1 #define TELNET_SB_X3_PAD_IS 2 #define TELNET_SB_X3_PAD_RESPONSE_IS 3 #define TELNET_SB_X3_PAD_SEND 4 /* RFC 1073 - TELNET windows size option */ #define TELNET_OPTION_NAWS 31 /* RFC 1079 - TELNET terminal speed */ #define TELNET_OPTION_TERMINAL_SPEED 32 #define TELNET_SB_TERMINAL_SPEED_IS 0 #define TELNET_SB_TERMINAL_SPEED_SEND 1 /* RFC 1372 - TELNET remote flow control */ #define TELNET_OPTION_TOGGLE_FLOW_CONTROL 33 /* RFC 1184 - TELNET linemode */ #define TELNET_OPTION_LINEMODE 34 #define TELNET_SB_LINEMODE_MODE 1 #define TELNET_SB_LINEMODE_FORWARDMASK 2 #define TELNET_SB_LINEMODE_SLC 3 /* RFC 1096 - TELNET X display location */ #define TELNET_OPTION_X_DISPLAY_LOCATION 35 #define TELNET_SB_X_DISPLAY_LOCATION_IS 0 #define TELNET_SB_X_DISPLAY_LOCATION_SEND 1 /* RFC 1408 - TELNET enviroment variable access - old */ #define TELNET_OPTION_OLD_ENVIRONMENT 36 #define TELNET_SB_OLD_ENVIRONMENT_IS 0 #define TELNET_SB_OLD_ENVIRONMENT_SEND 1 #define TELNET_SB_OLD_ENVIRONMENT_INFO 2 /* RFC 2941 - TELNET authentication */ #define TELNET_OPTION_AUTHENTICATION 37 #define TELNET_SB_AUTHENTICATION_IS 0 #define TELNET_SB_AUTHENTICATION_SEND 1 #define TELNET_SB_AUTHENTICATION_REPLY 2 #define TELNET_SB_AUTHENTICATION_NAME 3 /* RFC 2946 - TELNET encryption */ #define TELNET_OPTION_ENCRYPT 38 #define TELNET_SB_ENCRYPT_IS 0 #define TELNET_SB_ENCRYPT_SUPPORT 1 #define TELNET_SB_ENCRYPT_REPLY 2 #define TELNET_SB_ENCRYPT_START 3 #define TELNET_SB_ENCRYPT_END 4 #define TELNET_SB_ENCRYPT_REQUEST_START 5 #define TELNET_SB_ENCRYPT_REQUEST_END 6 #define TELNET_SB_ENCRYPT_ENC_KEYID 7 #define TELNET_SB_ENCRYPT_DEC_KEYID 8 /* RFC 1572 - TELNET environment option */ #define TELNET_OPTION_ENVIRONMENT 39 #define TELNET_SB_ENVIRONMENT_IS 0 #define TELNET_SB_ENVIRONMENT_SEND 1 #define TELNET_SB_ENVIRONMENT_INFO 2 #define TELNET_OPTARG_ENVIRONMENT_VAR 0 #define TELNET_OPTARG_ENVIRONMENT_VALUE 1 #define TELNET_OPTARG_ENVIRONMENT_ESC 2 #define TELNET_OPTARG_ENVIRONMENT_USERVAR 3 /* RFC 1647 - TELNET TN3270E terminal functions */ #define TELNET_OPTION_TN3270E 40 #define TELNET_SB_TN3270E_ASSOCIATE 0 #define TELNET_SB_TN3270E_CONNECT 1 #define TELNET_SB_TN3270E_DEVICE_TYPE 2 #define TELNET_SB_TN3270E_FUNCTIONS 3 #define TELNET_SB_TN3270E_IS 4 #define TELNET_SB_TN3270E_REASON 5 #define TELNET_SB_TN3270E_REJECT 6 #define TELNET_SB_TN3270E_REQUEST 7 #define TELNET_SB_TN3270E_SEND 8 /* RFC 2066 - TELNET character set support */ #define TELNET_OPTION_CHARSET 42 #define TELNET_SB_CHARSET_REQUEST 1 #define TELNET_SB_CHARSET_ACCEPTED 2 #define TELNET_SB_CHARSET_REJECTED 3 #define TELNET_SB_CHARSET_TTABLE_IS 4 #define TELNET_SB_CHARSET_TTABLE_REJECTED 5 #define TELNET_SB_CHARSET_TTABLE_ACK 6 #define TELNET_SB_CHARSET_TTABLE_NAK 7 /* RFC 2217 - TELNET serial port settings */ #define TELNET_OPTION_COM_PORT 44 #define TELNET_SB_COM_PORT_CLI_SET_BAUDRATE 1 #define TELNET_SB_COM_PORT_CLI_SET_DATASIZE 2 #define TELNET_SB_COM_PORT_CLI_SET_PARITY 3 #define TELNET_SB_COM_PORT_CLI_SET_STOPSIZE 4 #define TELNET_SB_COM_PORT_CLI_SET_CONTROL 5 #define TELNET_SB_COM_PORT_CLI_NOTIFY_LINESTATE 6 #define TELNET_SB_COM_PORT_CLI_NOTIFY_MODEMSTATE 7 #define TELNET_SB_COM_PORT_CLI_FLOWCONTROL_SUSPEND 8 #define TELNET_SB_COM_PORT_CLI_FLOWCONTROL_RESUME 9 #define TELNET_SB_COM_PORT_CLI_SET_LINESTATE_MASK 10 #define TELNET_SB_COM_PORT_CLI_SET_MODEMSTATE_MASK 11 #define TELNET_SB_COM_PORT_CLI_PURGE_DATA 12 #define TELNET_SB_COM_PORT_SVR_SET_BAUDRATE 101 #define TELNET_SB_COM_PORT_SVR_SET_DATASIZE 102 #define TELNET_SB_COM_PORT_SVR_SET_PARITY 103 #define TELNET_SB_COM_PORT_SVR_SET_STOPSIZE 104 #define TELNET_SB_COM_PORT_SVR_SET_CONTROL 105 #define TELNET_SB_COM_PORT_SVR_NOTIFY_LINESTATE 106 #define TELNET_SB_COM_PORT_SVR_NOTIFY_MODEMSTATE 107 #define TELNET_SB_COM_PORT_SVR_FLOWCONTROL_SUSPEND 108 #define TELNET_SB_COM_PORT_SVR_FLOWCONTROL_RESUME 109 #define TELNET_SB_COM_PORT_SVR_SET_LINESTATE_MASK 110 #define TELNET_SB_COM_PORT_SVR_SET_MODEMSTATE_MASK 111 #define TELNET_SB_COM_PORT_SVR_PURGE_DATA 112 /* RFC 2840 - TELNET KERMIT protocol */ #define TELNET_OPTION_KERMIT 47 #define TELNET_SB_KERMIT_START_SERVER 0 #define TELNET_SB_KERMIT_STOP_SERVER 1 #define TELNET_SB_KERMIT_REQ_START_SERVER 2 #define TELNET_SB_KERMIT_REQ_STOP_SERVER 3 #define TELNET_SB_KERMIT_SOP 4 #define TELNET_SB_KERMIT_RESP_START_SERVER 8 #define TELNET_SB_KERMIT_RESP_STOP_SERVER 9 /* RFC 861 - TELNET extended options list */ #define TELNET_OPTION_EXOPL 255 /* new commands */ #define TELNET_OPTION_CMD_EOF 236 #define TELNET_OPTION_CMD_SUSP 237 #define TELNET_OPTION_CMD_ABORT 238 /* proxy states */ #define TELNET_DATA 1 #define TELNET_GOT_IAC 2 #define TELNET_GOT_OPNEG 3 #define TELNET_GOT_SB 4 #define TELNET_IN_SB 5 #define TELNET_GOT_SB_IAC 6 #define TELNET_QUIT 7 /* option state codes */ #define SENT_WILL 1 /* 1/0: client sent WILL/WONT */ #define GOT_DO 2 /* 1/0: server sent DO/DONT */ /* option negotiation requests and responses */ #define WILL_OPTION 1 #define WONT_OPTION 2 #define DO_OPTION 3 #define DONT_OPTION 4 #define TELNET_BUFFER_SIZE 16384 #define TELNET_SUBOPTION_SIZE (TELNET_BUFFER_SIZE - 4) /* IAC SB code + 1 byte */ #define TELNET_CHECK_OK 1 #define TELNET_CHECK_REJECT 3 #define TELNET_CHECK_ABORT 4 #define TELNET_CHECK_DROP 5 /* policy constants */ #define TELNET_OPTION_ACCEPT 1 #define TELNET_OPTION_REJECT 3 #define TELNET_OPTION_ABORT 4 #define TELNET_OPTION_DROP 5 #define TELNET_OPTION_POLICY 6 /* macro to determine other endpoint */ #define OTHER_EP(x) ((EP_MAX - 1) - x) /* macro to convert endpoint number to string */ #define WHICH_EP(x) (((x) == EP_CLIENT) ? "client" : "server") #define TELNET_AUDIT_FORMAT_VERSION "0.0" struct _TelnetProxy; typedef guint (*TelnetOptionFunction)(struct _TelnetProxy *, guint); typedef struct _TelnetOptions { guint option; TelnetOptionFunction option_check; } TelnetOptions; typedef struct _ZIOBuffer { guchar buf[TELNET_BUFFER_SIZE+1]; gsize ofs, end; } ZIOBuffer; typedef struct _ZIOBufferDyn { guchar *buf; guint ofs, end, size; } ZIOBufferDyn; #define TELNET_REQUEST "telnet.request" #define TELNET_RESPONSE "telnet.response" #define TELNET_DEBUG "telnet.debug" #define TELNET_VIOLATION "telnet.violation" #define TELNET_ERROR "telnet.error" #define TELNET_POLICY "telnet.policy" #define TELNET_VIOLATION "telnet.violation" typedef struct _TelnetProxy { ZProxy super; /* Policy level variables */ /* timeout in milliseconds */ gint timeout; /* policy hash */ ZDimHashTable *telnet_policy; /* options <-> commands link hash keyed by the command */ GHashTable *negotiation; /* variables for the policy callbacks to be able to make changes */ GString *policy_name, *policy_value; /* Private variables */ gint state[EP_MAX]; gint ep; /* input buffers */ ZIOBuffer read_buffers[EP_MAX]; /* output buffers */ ZIOBufferDyn write_buffers[EP_MAX]; /* buffer to store suboption negotiation stream */ ZIOBuffer suboptions[EP_MAX]; /* option negotiation state */ gchar options[256][EP_MAX]; /* WILL_OPTION, WONT_OPTION, DO_OPTION or DONT_OPTION * shows what was requested in TELNET_GOT_OPNEG state * * NOTE: the code assumes that these are octet sized, please be sure to * audit the code if the type of these values is ever changed. */ guchar command[EP_MAX]; guchar opneg_option[EP_MAX]; /* suboption check function lookup table */ TelnetOptionFunction telnet_options[256]; ZPoll *poll; } TelnetProxy; gboolean telnet_collect_meta(TelnetProxy *self, const gchar *name, const gchar *value, gboolean commit) G_GNUC_WARN_UNUSED_RESULT; extern ZClass TelnetProxy__class; #endif zorp-3.9.5/modules/telnet/telnetoption.c000066400000000000000000000515221172670260400203670ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Hidden * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include "telnet.h" #include "telnetpolicy.h" #include #include #include #define TELNET_POLICY "telnet.policy" #define TELNET_DEBUG "telnet.debug" /* virtual variable names */ #define TELNET_POLICY_TERMTYPE_NAME "TERMINAL_TYPE" #define TELNET_POLICY_TERMSPEED_NAME "TERMINAL_SPEED" #define TELNET_POLICY_XDISPLAY_NAME "X_DISPLAY_LOCATION" #define TELNET_POLICY_NAWS_NAME "WINDOW_SIZE" /* size of buffers used in suboption processing functions */ #define SB_BUF_SIZE 512 /** * telnet_opt_terminal_type: * @self: * @ep: * * * * Returns: * */ guint telnet_opt_terminal_type(TelnetProxy *self, guint ep) { ZIOBuffer *sbuf = &self->suboptions[ep]; guint ptr, i; gchar buf[SB_BUF_SIZE]; guint res; z_proxy_enter(self); ptr = sbuf->ofs; if (sbuf->buf[ptr] == TELNET_SB_TERMINAL_TYPE_IS) { /* check if this side sent WILL */ if (!(self->options[self->opneg_option[ep]][ep] & SENT_WILL)) { z_proxy_log(self, TELNET_POLICY, 3, "TERMINAL TYPE IS option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } /* check if valid */ for (ptr++; ptr < sbuf->end; ptr++) if (!isalnum(sbuf->buf[ptr]) && sbuf->buf[ptr] != '-') { /* FIXME: the value should be logged */ z_proxy_log(self, TELNET_VIOLATION, 3, "Invalid TERMINAL TYPE value, it contains invalid characters;"); z_proxy_return(self, TELNET_CHECK_ABORT); } /* copy to buf */ for (i = 0, ptr = sbuf->ofs + 1; ptr < sbuf->end && i < sizeof(buf); ptr++, i++) buf[i] = sbuf->buf[ptr]; if (i >= sizeof(buf)) { z_proxy_log(self, TELNET_VIOLATION, 3, "Invalid TERMINAL TYPE value, it is too long;"); z_proxy_return(self, TELNET_CHECK_ABORT); } buf[i] = 0; z_proxy_log(self, TELNET_DEBUG, 6, "TERMINAL TYPE option; value='%s'", buf); g_string_assign(self->policy_name, TELNET_POLICY_TERMTYPE_NAME); g_string_assign(self->policy_value, buf); res = telnet_policy_suboption(self, 0, TELNET_POLICY_TERMTYPE_NAME, buf); if (res == TELNET_CHECK_OK) { for (i = 0, ptr = sbuf->ofs + 1; i < self->policy_value->len; i++, ptr++) sbuf->buf[ptr] = self->policy_value->str[i]; sbuf->end = ptr; } } else if (sbuf->buf[ptr] == TELNET_SB_TERMINAL_TYPE_SEND && sbuf->end == ptr + 1) { /* check if this side sent DO */ if (!(self->options[self->opneg_option[ep]][OTHER_EP(ep)] & GOT_DO)) { z_proxy_log(self, TELNET_POLICY, 3, "TERMINAL TYPE SEND option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } g_string_assign(self->policy_name, TELNET_POLICY_TERMTYPE_NAME); g_string_assign(self->policy_value, ""); res = telnet_policy_suboption(self, 1, TELNET_POLICY_TERMTYPE_NAME, ""); } else /* suboption code is INVALID */ { z_proxy_log(self, TELNET_VIOLATION, 3, "TERMINAL TYPE option, invalid subcommand or invalid suboption length;"); z_proxy_return(self, TELNET_CHECK_ABORT); } z_proxy_return(self, res); } /** * telnet_opt_terminal_speed: * @self: * @ep: * * * * Returns: * */ guint telnet_opt_terminal_speed(TelnetProxy *self, guint ep) { ZIOBuffer *sbuf = &self->suboptions[ep]; guint ptr, i; gchar buf[SB_BUF_SIZE]; guint res; z_proxy_enter(self); ptr = sbuf->ofs; if (sbuf->buf[ptr] == TELNET_SB_TERMINAL_SPEED_IS) { /* check if this side sent WILL */ if (!(self->options[self->opneg_option[ep]][ep] & SENT_WILL)) { z_proxy_log(self, TELNET_VIOLATION, 3, "TERMINAL SPEED IS option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } for (ptr++; ptr < sbuf->end; ptr++) { if (!isdigit(sbuf->buf[ptr]) && sbuf->buf[ptr] != ',') { z_proxy_log(self, TELNET_VIOLATION, 3, "TERMINAL SPEED IS option, invalid speed string;"); z_proxy_return(self, TELNET_CHECK_ABORT); } } for (i = 0, ptr = sbuf->ofs + 1; ptr < sbuf->end && i < sizeof(buf); ptr++, i++) buf[i] = sbuf->buf[ptr]; if (i >= sizeof(buf)) { z_proxy_log(self, TELNET_VIOLATION, 3, "TERMINAL SPEED IS option, value too long"); z_proxy_return(self, TELNET_CHECK_ABORT); } buf[i] = 0; z_proxy_log(self, TELNET_DEBUG, 6, "TERMINAL SPEED IS option; value='%s'", buf); g_string_assign(self->policy_name, TELNET_POLICY_TERMSPEED_NAME); g_string_assign(self->policy_value, buf); res = telnet_policy_suboption(self, 0, TELNET_POLICY_TERMSPEED_NAME, buf); if (res == TELNET_CHECK_OK) { for (i = 0, ptr = sbuf->ofs + 1; i < self->policy_value->len; i++, ptr++) sbuf->buf[ptr] = self->policy_value->str[i]; sbuf->end = ptr; } } else if (sbuf->buf[ptr] == TELNET_SB_TERMINAL_SPEED_SEND && sbuf->end == ptr + 1) { /* check if this side sent DO */ if (!(self->options[self->opneg_option[ep]][OTHER_EP(ep)] & GOT_DO)) { z_proxy_log(self, TELNET_VIOLATION, 3, "TERMINAL SPEED SEND option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } g_string_assign(self->policy_name, TELNET_POLICY_TERMSPEED_NAME); g_string_assign(self->policy_value, ""); res = telnet_policy_suboption(self, 1, TELNET_POLICY_TERMSPEED_NAME, ""); } else /* suboption code is INVALID */ { z_proxy_log(self, TELNET_VIOLATION, 3, "TERMINAL SPEED option, invalid subcommand;"); z_proxy_return(self, TELNET_CHECK_ABORT); } z_proxy_return(self, res); } /** * telnet_opt_x_display: * @self: * @ep: * * * * Returns: * */ guint telnet_opt_x_display(TelnetProxy *self, guint ep) { ZIOBuffer *sbuf = &self->suboptions[ep]; guint ptr, i; gchar buf[SB_BUF_SIZE]; guint res; z_proxy_enter(self); ptr = sbuf->ofs; if (sbuf->buf[ptr] == TELNET_SB_X_DISPLAY_LOCATION_IS) { /* check if this side sent WILL */ if (!(self->options[self->opneg_option[ep]][ep] & SENT_WILL)) { z_proxy_log(self, TELNET_VIOLATION, 3, "X DISPLAY LOCATION IS option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } for (ptr++; ptr < sbuf->end; ptr++) { if (!isalnum(sbuf->buf[ptr]) && strchr(".:_-", sbuf->buf[ptr]) == NULL) { z_proxy_log(self, TELNET_VIOLATION, 3, "X DISPLAY LOCATION IS option, it contains invalid characters; value='%.*s'", (gint) (sbuf->end - (sbuf->ofs + 1)), &sbuf->buf[sbuf->ofs + 1]); z_proxy_return(self, TELNET_CHECK_ABORT); } } for (i = 0, ptr = sbuf->ofs + 1; ptr < sbuf->end && i < sizeof(buf); ptr++, i++) buf[i] = sbuf->buf[ptr]; if (i >= sizeof(buf)) { z_proxy_log(self, TELNET_VIOLATION, 3, "X DISPLAY LOCATION IS option, value too long;"); z_proxy_return(self, TELNET_CHECK_ABORT); } buf[i] = 0; z_proxy_log(self, TELNET_DEBUG, 6, "X DISPLAY LOCATION IS option; value='%s'", buf); g_string_assign(self->policy_name, TELNET_POLICY_XDISPLAY_NAME); g_string_assign(self->policy_value, buf); res = telnet_policy_suboption(self, 0, TELNET_POLICY_XDISPLAY_NAME, buf); if (res == TELNET_CHECK_OK) { for (i = 0, ptr = sbuf->ofs + 1; i < self->policy_value->len; i++, ptr++) sbuf->buf[ptr] = self->policy_value->str[i]; sbuf->end = ptr; } } else if (sbuf->buf[ptr] == TELNET_SB_X_DISPLAY_LOCATION_SEND && sbuf->end == ptr + 1) { /* check if this side sent DO */ if (!(self->options[self->opneg_option[ep]][OTHER_EP(ep)] & GOT_DO)) { z_proxy_log(self, TELNET_VIOLATION, 3, "X DISPLAY LOCATION SEND option is not allowed from this side;"); z_proxy_return(self, TELNET_CHECK_ABORT); } g_string_assign(self->policy_name, TELNET_POLICY_XDISPLAY_NAME); g_string_assign(self->policy_value, ""); res = telnet_policy_suboption(self, 1, TELNET_POLICY_XDISPLAY_NAME, ""); } else /* suboption code is INVALID */ { z_proxy_log(self, TELNET_VIOLATION, 3, "X DISPLAY LOCATION option, invalid subcommand or invalid suboption length;"); z_proxy_return(self, TELNET_CHECK_ABORT); } z_proxy_return(self, res); } /** * telnet_opt_new_env: * @self: * @ep: * * * * Returns: * */ guint telnet_opt_new_env(TelnetProxy *self, guint ep) { ZIOBuffer *sbuf = &self->suboptions[ep]; ZIOBuffer cbuf; guint ptr, i; guint res; gchar name[SB_BUF_SIZE], value[SB_BUF_SIZE]; guchar command, type; gboolean valid = FALSE; /* TRUE if there was anything accepted by policy */ z_proxy_enter(self); ptr = sbuf->ofs++; command = sbuf->buf[ptr++]; /* initialize cbuf */ cbuf.ofs = 0; cbuf.end = 1; cbuf.buf[0] = command; if (command == TELNET_SB_ENVIRONMENT_IS || command == TELNET_SB_ENVIRONMENT_INFO) { /* check if this side sent WILL */ if (!(self->options[self->opneg_option[ep]][ep] & SENT_WILL)) { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW ENVIRON IS or INFO option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } if (ptr == sbuf->end) { /* if this was an empty IS or INFO reply */ g_string_assign(self->policy_name, ""); g_string_assign(self->policy_value, ""); res = telnet_policy_suboption(self, command, "", ""); if (res == TELNET_CHECK_OK) valid = TRUE; } else while (ptr < sbuf->end) { switch (type = sbuf->buf[ptr++]) { case TELNET_OPTARG_ENVIRONMENT_VAR: case TELNET_OPTARG_ENVIRONMENT_USERVAR: /* initialize variable name and value buffers */ name[0] = '\0'; value[0] = '\0'; for (i = 0; ptr < sbuf->end && i < sizeof(name); ptr++, i++) { if (sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_VAR || sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_VALUE || sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_USERVAR) break; /* got VAR, VALUE, or USERVAR, here ends variable name */ if (sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_ESC || sbuf->buf[ptr] == TELNET_IAC) ptr++; /* got ESC or IAC, these are followed by the real byte */ if (i < sizeof(name)) name[i] = sbuf->buf[ptr]; } /* terminate name */ if (i < sizeof(name)) name[i] = '\0'; else { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON option, variable name too long;"); ptr = sbuf->end; break; /* switch */ } if (ptr < sbuf->end && sbuf->buf[ptr++] == TELNET_OPTARG_ENVIRONMENT_VALUE) { /* if next byte is VALUE */ for (i = 0; ptr < sbuf->end && i < sizeof(value); ptr++, i++) { if (sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_VAR || sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_VALUE || sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_USERVAR) break; /* got VAR, VALUE or USERVAR, here ends value */ if (sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_ESC || sbuf->buf[ptr] == TELNET_IAC) ptr++; /* got ESC or IAC, these are followed by the real byte */ if (ptr < sbuf->end) value[i] = sbuf->buf[ptr]; } /* terminate value */ if (i < sizeof(value)) { value[i] = '\0'; } else { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON option, variable value too long;"); ptr = sbuf->end; break; /* switch */ } } z_proxy_log(self, TELNET_DEBUG, 6, "NEW-ENVIRON; subopt='%s', name='%s', value='%s'", (command == TELNET_SB_ENVIRONMENT_IS) ? "IS" : "INFO", name, value); g_string_assign(self->policy_name, name); g_string_assign(self->policy_value, value); res = telnet_policy_suboption(self, command, name, value); if (res == TELNET_CHECK_OK) { valid = TRUE; /* we may forward variable, so copy it */ cbuf.buf[cbuf.end++] = type; if (cbuf.end < sizeof(cbuf.buf)) { for (i = 0; cbuf.end < sizeof(cbuf.buf) && i < self->policy_name->len; i++, cbuf.end++) cbuf.buf[cbuf.end] = self->policy_name->str[i]; } if (cbuf.end < sizeof(cbuf.buf)) cbuf.buf[cbuf.end++] = 1; if (cbuf.end < sizeof(cbuf.buf)) { for (i = 0; cbuf.end < sizeof(cbuf.buf) && i < self->policy_value->len; i++, cbuf.end++) cbuf.buf[cbuf.end] = self->policy_value->str[i]; } if (cbuf.end >= sizeof(cbuf.buf)) { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON option, variable buffer full;"); res = TELNET_CHECK_DROP; valid = FALSE; break; } } sbuf->ofs = ptr; break; default: /* not VAR or USERVAR, invalid */ z_proxy_log(self, TELNET_VIOLATION, 5, "NEW-ENVIRON IS or INFO option, invalid reply;"); /* set pointer to the end of sbuf, so that while terminates */ ptr = sbuf->end; break; } /* switch */ } /* while */ } /* if */ else if (command == TELNET_SB_ENVIRONMENT_SEND) { /* check if this side sent DO */ if (!(self->options[self->opneg_option[ep]][OTHER_EP(ep)] & GOT_DO)) { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON SEND otpion not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } if (ptr == sbuf->end) { /* if this was an empty SEND request */ g_string_assign(self->policy_name, ""); g_string_assign(self->policy_value, ""); res = telnet_policy_suboption(self, command, "", ""); if (res == TELNET_CHECK_OK) valid = TRUE; } else while (ptr < sbuf->end) { switch (type = sbuf->buf[ptr++]) { case TELNET_OPTARG_ENVIRONMENT_VAR: case TELNET_OPTARG_ENVIRONMENT_USERVAR: /* initialize variable name */ name[0] = '\0'; for (i = 0; ptr < sbuf->end && i < sizeof(name); ptr++, i++) { if (sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_VAR || sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_USERVAR) break; /* got VAR or USERVAR, here ends variable name */ if (sbuf->buf[ptr] == TELNET_OPTARG_ENVIRONMENT_ESC || sbuf->buf[ptr] == TELNET_IAC) ptr++; /* got ESC or IAC, these are followed by the real byte */ if (i < sizeof(name)) name[i] = sbuf->buf[ptr]; } /* terminate name */ if (i < sizeof(name)) { name[i] = '\0'; } else { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON SEND option, variable name too long"); ptr = sbuf->end; break; } z_proxy_log(self, TELNET_DEBUG, 6, "NEW-ENVIRON SEND option; value='%s'", name); g_string_assign(self->policy_name, name); g_string_assign(self->policy_value, ""); res = telnet_policy_suboption(self, command, name, ""); if (res == TELNET_CHECK_OK) { valid = TRUE; /* we may forward option, so copy it */ for (; sbuf->ofs < ptr; sbuf->ofs++, cbuf.end++) cbuf.buf[cbuf.end] = sbuf->buf[sbuf->ofs]; } else { sbuf->ofs = ptr; } break; default: /* not VAR or USERVAR, invalid */ z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON option, invalid SEND request;"); ptr = sbuf->end; break; } } /* while */ } else /* suboption code is INVALID */ { z_proxy_log(self, TELNET_VIOLATION, 3, "NEW-ENVIRON option, invalid subcommand;"); } /* if there wasn't any accepted variable, return DENY */ if (!valid) z_proxy_return(self, TELNET_CHECK_ABORT); /* else copy accepted bytes to suboption buffer */ for (i = 0; i < cbuf.end; i++) sbuf->buf[i] = cbuf.buf[i]; sbuf->ofs = 0; sbuf->end = cbuf.end; z_proxy_return(self, TELNET_CHECK_OK); } /** * telnet_opt_naws: * @self: * @ep: * * * * Returns: * */ guint telnet_opt_naws(TelnetProxy *self, guint ep) { ZIOBuffer *sbuf = &self->suboptions[ep]; guint ptr, i; guchar buf[SB_BUF_SIZE]; guint res; gchar value[SB_BUF_SIZE]; guint16 width, height; gchar width_cols[256], height_rows[256]; z_proxy_enter(self); /* check if this side sent WILL */ if (!(self->options[self->opneg_option[ep]][ep] & SENT_WILL)) { z_proxy_log(self, TELNET_DEBUG, 5, "NAWS option not allowed from this side; side='%s'", WHICH_EP(ep)); z_proxy_return(self, TELNET_CHECK_ABORT); } if (sbuf->end - sbuf->ofs != 4) { for (ptr = sbuf->ofs, i = 0; ptr < sbuf->end && i < sizeof(buf); ptr++, i++) { buf[i] = sbuf->buf[ptr]; if (sbuf->buf[ptr] == TELNET_IAC) ptr++; } if (i != 4) { z_proxy_log(self, TELNET_VIOLATION, 3, "NAWS option, invalid length"); z_proxy_return(self, TELNET_CHECK_ABORT); } } else { for (ptr = sbuf->ofs, i = 0; i < 4; ptr++, i++) buf[i] = sbuf->buf[ptr]; } width = (buf[0] << 8) + buf[1]; height = (buf[2] << 8) + buf[3]; g_string_assign(self->policy_name, TELNET_POLICY_NAWS_NAME); g_string_sprintf(self->policy_value, "%hd,%hd", width, height); snprintf(value, sizeof(value), "%hd,%hd", width, height); res = telnet_policy_suboption(self, 0, TELNET_POLICY_NAWS_NAME, value); if (res == ZV_ACCEPT) { g_snprintf(width_cols, sizeof(width_cols), "%hd", width); g_snprintf(height_rows, sizeof(height_rows), "%hd", height); } z_proxy_return(self, res); } zorp-3.9.5/modules/telnet/telnetoption.h000066400000000000000000000030521172670260400203670ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #ifndef ZORP_MODULES_TELNETOPTION_H_INCLUDED #define ZORP_MODULES_TELNETOPTION_H_INCLUDED #include "telnet.h" guint telnet_opt_terminal_type(TelnetProxy *self, guint ep); guint telnet_opt_terminal_speed(TelnetProxy *self, guint ep); guint telnet_opt_x_display(TelnetProxy *self, guint ep); guint telnet_opt_new_env(TelnetProxy *self, guint ep); guint telnet_opt_naws(TelnetProxy *self, guint ep); #endif zorp-3.9.5/modules/telnet/telnetpolicy.c000066400000000000000000000243641172670260400203620ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Hidden * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include "telnet.h" #define TELNET_POLICY "telnet.policy" #define TELNET_DEBUG "telnet.debug" /** * telnet_hash_get_type: * @tuple: * @filter_type: * * * * Returns: * */ gboolean telnet_hash_get_type(ZPolicyObj *tuple, guint *filter_type) { ZPolicyObj *tmp; gboolean res; if (!z_policy_seq_check(tuple)) { res = z_policy_var_parse(tuple, "i", filter_type); } else { tmp = z_policy_seq_getitem(tuple, 0); res = z_policy_var_parse(tmp, "i", filter_type); /* FALSE -> policy syntax error */ z_policy_var_unref(tmp); } return res; } /** * telnet_policy_option: * @self: * * * * Returns: * */ guint telnet_policy_option(TelnetProxy *self) { guint res; ZPolicyObj *pol_res; ZPolicyObj *tmp; ZPolicyObj *command_where = NULL; guint command_do; gchar lookup_str[10]; gchar *keys[1]; gboolean type_found; z_proxy_enter(self); z_proxy_log(self, TELNET_DEBUG, 8, "Policy option negotiation check; option='%d'", self->opneg_option[self->ep]); g_snprintf(lookup_str, sizeof(lookup_str), "%d", self->opneg_option[self->ep]); keys[0] = lookup_str; tmp = z_dim_hash_table_search(self->telnet_policy, 1, keys); if (!tmp) { z_proxy_log(self, TELNET_POLICY, 2, "Option not found in policy; option='%s'", lookup_str); z_proxy_return(self, TELNET_CHECK_DROP); } z_policy_lock(self->super.thread); type_found = telnet_hash_get_type(tmp, &command_do); z_policy_unlock(self->super.thread); if (!type_found ) { z_proxy_log(self, TELNET_POLICY, 2, "Policy type invalid; option='%s'", lookup_str); z_proxy_return(self, TELNET_CHECK_ABORT); } switch (command_do) { case TELNET_OPTION_DROP: z_proxy_log(self, TELNET_POLICY, 3, "Policy denied option; option='%s'", lookup_str); res = TELNET_CHECK_DROP; break; case TELNET_OPTION_ACCEPT: z_proxy_log(self, TELNET_POLICY, 6, "Policy accepted option; option='%s'", lookup_str); res = TELNET_CHECK_OK; break; case TELNET_OPTION_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp, "(iO)", &command_do, &command_where)) { z_proxy_log(self, TELNET_POLICY, 2, "Cannot parse policy line; option='%s'", lookup_str); res = TELNET_CHECK_ABORT; } else { pol_res = z_policy_call_object(command_where, z_policy_var_build("(i)", &self->opneg_option[self->ep]), self->super.session_id); if (pol_res == NULL) { z_proxy_log(self, TELNET_POLICY, 2, "Error in policy calling; option='%s'", lookup_str); res = TELNET_CHECK_ABORT; } else if (!z_policy_var_parse(pol_res, "i", &res)) { z_proxy_log(self, TELNET_POLICY, 1, "Can't parse return verdict; option='%s'", lookup_str); res = TELNET_CHECK_ABORT; } else { switch (res) { case ZV_ACCEPT: z_proxy_log(self, TELNET_POLICY, 6, "Policy function accepted option; option='%s'", lookup_str); res = TELNET_CHECK_OK; break; case ZV_UNSPEC: case ZV_DROP: z_proxy_log(self, TELNET_POLICY, 3, "Policy function drop option; option='%s'", lookup_str); res = TELNET_CHECK_DROP; break; case TELNET_OPTION_REJECT: z_proxy_log(self, TELNET_POLICY, 3, "Policy function reject option; option='%s'", lookup_str); res = TELNET_CHECK_REJECT; break; case ZV_ABORT: default: z_proxy_log(self, TELNET_POLICY, 1, "Policy function aborted session; option='%s'", lookup_str); res = TELNET_CHECK_ABORT; break; } } } z_policy_unlock(self->super.thread); break; case TELNET_OPTION_REJECT: z_proxy_log(self, TELNET_POLICY, 3, "Policy rejected option; option='%s'", lookup_str); res = TELNET_CHECK_REJECT; break; case TELNET_OPTION_ABORT: default: z_proxy_log(self, TELNET_POLICY, 3, "Policy aborted session; option='%s'", lookup_str); res = TELNET_CHECK_ABORT; break; } z_proxy_return(self, res); } /** * telnet_policy_suboption: * @self: * @command: * @name: * @value: * * * * Returns: * */ guint telnet_policy_suboption(TelnetProxy *self, guchar command, gchar *name, gchar *value) { guint res; ZPolicyObj *pol_res; ZPolicyObj *tmp; ZPolicyObj *command_where = NULL; guint command_do; gchar lookup_str[2][10]; gchar *keys[2]; gboolean type_found; z_proxy_enter(self); z_proxy_log(self, TELNET_DEBUG, 8, "Policy suboption negotiation check;"); g_snprintf(lookup_str[0], sizeof(lookup_str[0]), "%d", self->opneg_option[self->ep]); g_snprintf(lookup_str[1], sizeof(lookup_str[1]), "%d", command); keys[0] = lookup_str[0]; keys[1] = lookup_str[1]; tmp = z_dim_hash_table_search(self->telnet_policy, 2, keys); if (!tmp) { z_proxy_log(self, TELNET_POLICY, 1, "Option not found in policy hash, dropping; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); z_proxy_return(self, TELNET_CHECK_DROP); } z_policy_lock(self->super.thread); type_found = telnet_hash_get_type(tmp, &command_do); z_policy_unlock(self->super.thread); if (!type_found) { z_proxy_log(self, TELNET_POLICY, 2, "Policy type invalid!"); z_proxy_return(self, TELNET_CHECK_ABORT); } switch (command_do) { case TELNET_OPTION_DROP: z_proxy_log(self, TELNET_POLICY, 6, "Policy denied suboption; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_DROP; break; case TELNET_OPTION_ACCEPT: z_proxy_log(self, TELNET_POLICY, 6, "Policy accepted suboption; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_OK; break; case TELNET_OPTION_POLICY: z_policy_lock(self->super.thread); if (!z_policy_var_parse(tmp, "(iO)", &command_do, &command_where)) { z_proxy_log(self, TELNET_POLICY, 2, "Cannot parse policy line for option; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_ABORT; } else { /* call Python method with appropriate parameters */ switch (self->opneg_option[self->ep]) { case TELNET_OPTION_TERMINAL_TYPE: case TELNET_OPTION_TERMINAL_SPEED: case TELNET_OPTION_X_DISPLAY_LOCATION: case TELNET_OPTION_ENVIRONMENT: case TELNET_OPTION_NAWS: pol_res = z_policy_call_object(command_where, z_policy_var_build("(iss)", &self->opneg_option[self->ep], name, value), self->super.session_id); break; default: pol_res = z_policy_call_object(command_where, z_policy_var_build("(i)", &self->opneg_option[self->ep]), self->super.session_id); break; } if (pol_res == NULL) { z_proxy_log(self, TELNET_POLICY, 2, "Error in policy calling; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_ABORT; } else if (!z_policy_var_parse(pol_res, "i", &res)) { z_proxy_log(self, TELNET_POLICY, 2, "Can't parse return verdict; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_ABORT; } else { switch (res) { case ZV_ACCEPT: z_proxy_log(self, TELNET_POLICY, 6, "Policy function accepted suboption; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_OK; break; case ZV_UNSPEC: case ZV_REJECT: case ZV_DROP: z_proxy_log(self, TELNET_POLICY, 3, "Policy function denied suboption; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_DROP; break; case ZV_ABORT: default: z_proxy_log(self, TELNET_POLICY, 3, "Policy function aborted suboption; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_ABORT; break; } } } z_policy_unlock(self->super.thread); break; case TELNET_OPTION_ABORT: default: z_proxy_log(self, TELNET_POLICY, 3, "Policy aborted session; command=`%s', option=`%s'", lookup_str[1], lookup_str[0]); res = TELNET_CHECK_ABORT; break; } z_proxy_return(self, res); } zorp-3.9.5/modules/telnet/telnetpolicy.h000066400000000000000000000027301172670260400203600ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Hidden * Auditor: * Last audited version: * Notes: ***************************************************************************/ #ifndef ZORP_MODULES_TELNETPOLICY_H_INCLUDED #define ZORP_MODULES_TELNETPOLICY_H_INCLUDED #include "telnet.h" guint telnet_policy_option(TelnetProxy *self); guint telnet_policy_suboption(TelnetProxy *self, guchar command, gchar *name, gchar* value); #endif zorp-3.9.5/modules/whois/000077500000000000000000000000001172670260400153305ustar00rootroot00000000000000zorp-3.9.5/modules/whois/Makefile.am000066400000000000000000000003551172670260400173670ustar00rootroot00000000000000pkgdatadir = @datadir@/zorp/pylib/Zorp pkglibdir = @libdir@/zorp LIBS = @MODULES_LIBS@ CPPFLAGS = @MODULES_CPPFLAGS@ pkgdata_DATA = Whois.py pkglib_LTLIBRARIES = libwhois.la libwhois_la_SOURCES = whois.c EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/modules/whois/Whois.py000066400000000000000000000205261172670260400170000ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## Author : Bazsi ## Auditor : ## Last audited version: 1.1 ## Notes: ## ############################################################################ """ Proxy for the Whois information lookup protocol. WHOIS is a protocol providing information about domain and IP owners.
The Whois protocol Whois is a netwide service to the Internet users maintained by DDN Network Information Center (NIC). The protocol follows a very simple method. First the client opens a TCP connection to the server at the port 43 and sends a one line REQUEST closed with <CRLF>. This request can contain only ASCII characters. The server sends the result back and closes the connection.
Proxy behavior WhoisProxy is a module build for parsing messages of the WHOIS protocol. It reads and parses the REQUESTs on the client side and sends them to the server if the local security policy permits. Arriving RESPONSEs are not parsed as they do not have any fixed structure or syntax. Example WhoisProxy logging all whois requests class MyWhoisProxy(AbstractWhoisProxy): def whoisRequest(self, request): log(None, CORE_DEBUG, 3, "Whois request: '%s'" % (request)) return ZV_ACCEPT
Related standards The NICNAME/WHOIS protocol is described in RFC 954.
""" from Zorp import * from Proxy import Proxy class AbstractWhoisProxy(Proxy): """ Class encapsulating the abstract Whois proxy. This class implements the WHOIS protocol as specified in RFC 954. timeout 30000 I/O timeout in milliseconds. max_line_length 132 Maximum number of characters allowed in a single line. max_request_length 128 Maximum allowed length of a Whois request. request The Whois request. response_header Prepend this string to each Whois response. response_footer Append this string to each Whois response. """ name = "whois" def __init__(self, session): """ Constructor to initialize a WhoisProxy instance. This constructor creates and set up a WhoisProxy instance. session SESSION session this instance belongs to """ Proxy.__init__(self, session) def whoisRequest(self, request): """ Function to process whois requests. This function is called by the Whois proxy to process the requests. It can also be used to change specific attributes of the request. """ """ Arguments self -- this instance request -- request contents, same as self.request FIXME: xml-isation """ return ZV_ACCEPT class WhoisProxy(AbstractWhoisProxy): """ Default proxy class based on AbstractWhoisProxy. A default proxy class based on AbstractWhoisProxy. """ pass zorp-3.9.5/modules/whois/whois.c000066400000000000000000000254501172670260400166330ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Bazsi * Auditor: * Last audited version: * Notes: * ***************************************************************************/ #include #include #include #include #include #include #include #include #include /* log classes used by this module */ #define WHOIS_DEBUG "whois.debug" #define WHOIS_ERROR "whois.error" #define WHOIS_POLICY "whois.policy" #define WHOIS_REQUEST "whois.request" /*+ State information of the Whois proxy. +*/ typedef struct _WhoisProxy { ZProxy super; gint timeout; gint max_request_length; gint max_line_length; GString *request; GString *response_header; GString *response_footer; } WhoisProxy; extern ZClass WhoisProxy__class; /*+ Fill in our state with default values. +*/ static void whois_config_set_defaults(WhoisProxy *self) { z_proxy_enter(self); self->max_request_length = 128; self->max_line_length = 132; self->request = g_string_sized_new(0); self->response_header = g_string_sized_new(0); self->response_footer = g_string_sized_new(0); self->timeout = 30000; z_proxy_return(self); } /*+ Register variables exported to the policy layer. +*/ static void whois_register_vars(WhoisProxy *self) { z_proxy_enter(self); z_proxy_var_new(&self->super, "timeout", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->timeout); z_proxy_var_new(&self->super, "max_line_length", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->max_line_length); z_proxy_var_new(&self->super, "max_request_length", Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_INT, &self->max_request_length); z_proxy_var_new(&self->super, "request", Z_VAR_GET | Z_VAR_SET | Z_VAR_TYPE_STRING, self->request); z_proxy_var_new(&self->super, "response_header", Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_STRING, self->response_header); z_proxy_var_new(&self->super, "response_footer", Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_TYPE_STRING, self->response_footer); z_proxy_return(self); } /*+ Initialize config that python gave us. For now it's empty. +*/ static void whois_config_init(WhoisProxy *self G_GNUC_UNUSED) { /* should initialize self based on settings previously set by the config event handler */ } /*+ Initialize our client stream. We allocate a readline instance so that we can fetch input line by line. +*/ static gboolean whois_init_client_stream(WhoisProxy *self) { ZStream *tmpstream; z_proxy_enter(self); self->super.endpoints[EP_CLIENT]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_CLIENT]; self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_proxy_return(self, TRUE); } /*+ Initialize our server stream. Exit with an error if our server side is not connected. (ie NULL) +*/ static gboolean whois_init_server_stream(WhoisProxy *self) { ZStream *tmpstream; z_proxy_enter(self); if (!self->super.endpoints[EP_SERVER]) z_proxy_return(self, FALSE); self->super.endpoints[EP_SERVER]->timeout = self->timeout; tmpstream = self->super.endpoints[EP_SERVER]; self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF); z_stream_unref(tmpstream); z_proxy_return(self, TRUE); } /*+ Read and process a request. +*/ static gboolean whois_fetch_request(WhoisProxy *self) { gchar *line; gsize line_length; gint res; z_proxy_enter(self); res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &line, &line_length, NULL); if (res != G_IO_STATUS_NORMAL) { z_proxy_log(self, WHOIS_ERROR, 1, "Empty request received or I/O error;"); z_proxy_return(self, FALSE); } z_proxy_log(self, WHOIS_REQUEST, 7, "Incoming request; line='%.*s'", (gint) line_length, line); if (line_length > (guint) self->max_request_length) { z_proxy_log(self, WHOIS_REQUEST, 6, "Request too long; length='%zd', max_request_length='%d'", line_length, self->max_request_length); z_proxy_return(self, FALSE); } g_string_truncate(self->request, 0); g_string_append_len(self->request, line, line_length); z_proxy_return(self, TRUE); } /*+ Construct and send a request to the server based on the state stored by whois_fetch_request(). +*/ static gboolean whois_send_request(WhoisProxy *self) { gchar request[self->request->len + 6]; gsize bytes_written; z_proxy_enter(self); g_snprintf(request, sizeof(request), "%s\r\n", self->request->str); if (z_stream_write(self->super.endpoints[EP_SERVER], request, strlen(request), &bytes_written, NULL) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); z_proxy_return(self, TRUE); } /*+ Copy server's response to the client. +*/ static gboolean whois_copy_response(WhoisProxy *self) { gsize bytes_written; z_proxy_enter(self); if (self->response_header->len && z_stream_write(self->super.endpoints[EP_CLIENT], self->response_header->str, self->response_header->len, &bytes_written, NULL) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); while (1) { gchar *line; gsize line_len; gint res; gchar *response; if (!z_proxy_loop_iteration(&self->super)) break; res = z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &line_len, NULL); if (res != G_IO_STATUS_NORMAL) /* EOF or read error */ break; response = alloca(line_len + 3); memcpy(response, line, line_len); strcpy(response + line_len, "\r\n"); if (z_stream_write(self->super.endpoints[EP_CLIENT], response, line_len + 2, &bytes_written, NULL) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); } if (self->response_footer->len && z_stream_write(self->super.endpoints[EP_CLIENT], self->response_footer->str, self->response_footer->len, &bytes_written, NULL) != G_IO_STATUS_NORMAL) z_proxy_return(self, FALSE); z_proxy_return(self, TRUE); } static gboolean whois_query_policy(WhoisProxy *self) { char *errmsg = "Policy violation, request denied.\r\n"; gsize bytes_written; gint res; z_proxy_enter(self); z_policy_lock(self->super.thread); res = z_policy_event(self->super.handler, "whoisRequest", z_policy_var_build("(s)", self->request->str), self->super.session_id); switch (res) { case ZV_REJECT: case ZV_ABORT: z_stream_write(self->super.endpoints[EP_CLIENT], errmsg, strlen(errmsg), &bytes_written, NULL); /* fallthrough */ case ZV_DROP: z_policy_unlock(self->super.thread); z_proxy_return(self, FALSE); case ZV_UNSPEC: case ZV_ACCEPT: default: break; } z_policy_unlock(self->super.thread); z_proxy_return(self, TRUE); } static gboolean whois_config(ZProxy *s) { WhoisProxy *self = Z_CAST(s, WhoisProxy); gboolean success = FALSE; z_proxy_enter(self); whois_config_set_defaults(self); whois_register_vars(self); if (Z_SUPER(s, ZProxy)->config(s)) { whois_config_init(self); success = TRUE; } z_proxy_return(self, success); } static void whois_main(ZProxy *s) { WhoisProxy *self = Z_CAST(s, WhoisProxy); z_proxy_enter(self); if (!whois_init_client_stream(self)) z_proxy_return(self); z_proxy_log(self, WHOIS_DEBUG, 6, "fetching request;"); if (!whois_fetch_request(self)) { char *errmsg = "Whois protocol error or disallowed protocol element, request denied.\r\n"; gsize bytes_written; z_stream_write(self->super.endpoints[EP_CLIENT], errmsg, strlen(errmsg), &bytes_written, NULL); z_proxy_return(self); } z_proxy_log(self, WHOIS_DEBUG, 6, "checking policy;"); if (!whois_query_policy(self)) z_proxy_return(self); z_proxy_log(self, WHOIS_DEBUG, 6, "connecting to server;"); /* this sets the server side endpoint if successful */ if (!z_proxy_connect_server(&self->super, NULL, 0)) z_proxy_return(self); if (!whois_init_server_stream(self)) z_proxy_return(self); z_proxy_log(self, WHOIS_DEBUG, 6, "sending request;"); if (!whois_send_request(self)) z_proxy_return(self); z_proxy_log(self, WHOIS_DEBUG, 6, "copying response;"); if (!whois_copy_response(self)) z_proxy_return(self); z_proxy_log(self, WHOIS_DEBUG, 6, "processing done;"); z_proxy_return(self); } /*+ Whois proxy constructor. Allocates and initializes a proxy instance, starts proxy thread. +*/ static ZProxy * whois_proxy_new(ZProxyParams *params) { WhoisProxy *self; z_enter(); self = Z_CAST(z_proxy_new(Z_CLASS(WhoisProxy), params), WhoisProxy); z_return((ZProxy *) self); } static void whois_proxy_free(ZObject *s) { WhoisProxy *self G_GNUC_UNUSED = Z_CAST(s, WhoisProxy); z_enter(); z_proxy_free_method(s); z_return(); } ZProxyFuncs whois_proxy_funcs = { { Z_FUNCS_COUNT(ZProxy), whois_proxy_free, }, .config = whois_config, .main = whois_main, NULL }; Z_CLASS_DEF(WhoisProxy, ZProxy, whois_proxy_funcs); /*+ Module initialization function. Registers a new proxy type. +*/ gint zorp_module_init(void) { z_registry_add("whois", ZR_PROXY, whois_proxy_new); return TRUE; } zorp-3.9.5/pylib/000077500000000000000000000000001172670260400136465ustar00rootroot00000000000000zorp-3.9.5/pylib/Makefile.am000066400000000000000000000001471172670260400157040ustar00rootroot00000000000000 SUBDIRS = Zorp kznf pkgdatadir = @datadir@/zorp pkgdata_DATA = policy.boot EXTRA_DIST = policy.boot zorp-3.9.5/pylib/Zorp/000077500000000000000000000000001172670260400146005ustar00rootroot00000000000000zorp-3.9.5/pylib/Zorp/Auth.py000066400000000000000000001165121172670260400160610ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ## $Id: Auth.py,v 1.69 2004/05/07 12:57:53 bazsi Exp $ ## ## Author : Bazsi ## Auditor : kisza ## Last audited version: 1.3 ## Notes: ## ############################################################################ """ Module defining interface to the Authentication module. This module contains classes related to authentication and authorization. Together with the AuthDB module it implements the Authentication and Authorization framework of Zorp. User authentication verifies the identity of the user trying to access a particular network service. When performed on the connection level, that enables the full auditing of the network traffic. Authentication is often used in conjunction with authorization, allowing access to a service only to clients who have the right to do so.
Authentication and authorization basics Authentication is a method to ensure that certain services (access to a server, etc.) can be used only by the clients allowed to access the service. The process generally called as authentication actually consists of three distinct steps: Identification: Determining the clients identity (e.g.: requesting a username). Authentication: Verifying the clients identity (e.g.: requesting a password that only the real client knows). Authorization: Granting access to the service (e.g.: verifying that the authenticated client is allowed to access the service). It is important to note that although authentication and authorization are usually used together, they can also be used independently. Authentication verifies the identity of the client. There are situations where authentication is sufficient, because all users are allowed to access the services, only the event and the user's identity has to be logged. On the other hand, authorization is also possible without authentication, for example if access to a service is time-limited (e.g.: it can only be accessed outside the normal work-hours, etc.). In such situations authentication is not needed.
Authentication and authorization in Zorp Zorp can authenticate and authorize access to the Zorp services. The aim of authentication is to identify the user and the associated group memberships. When the client initiates a connection, it actually tries to use a Zorp service. Zorp checks if an authentication policy is associated to the service. If an authentication policy is present, Zorp contacts the authentication provider specified in the authentication policy. The type of authentication (the authentication class used, e.g., InbandAuthentication) is also specified in the authentication policy. The authentication provider connects to an authentication backend (e.g., a user database) to perform the authentication of the client - Zorp itself does not directly communicate with the database. If the authentication is successful, Zorp verifies that the client is allowed to access the service (by evaluating the authorization policy and the identity and group memberships of the client). If the client is authorized to access the service, the server-side connection is built. The client is automatically authorized if no authorization policy is assigned to the service. Currently only one authentication provider, the Zorp Authentication Server (ZAS) is available via the ZAS2AuthenticationBackend class. Authentication providers are actually configured instances of the authentication backends, and it is independent from the database that the backend connects to. The authentication backend is that ties the authentication provider to the server storing the user data. For details on using ZAS, see the Connection authentication and authorization chapter of the Zorp Administrator's Guide. The aim of authentication is to identify the user and resolve group memberships. The results are stored in the in the auth_user and auth_groups attributes of the session object. Note that apart from the information required for authentication, Zorp also sends session information (e.g., the IP address of the client) to the authentication provider. Zorp provides the following authentication classes: InbandAuthentication: Use the built-in authentication of the protocol to authenticate the client on the Zorp. ServerAuthentication: Enable the client to connect to the target server, and extract its authentication information from the protocol. ZAAuthentication: Outband authentication using the Zorp Authentication Agent. If the authentication is successful, Zorp verifies that the client is allowed to access the service (by evaluating the authorization policy). If the client is authorized to access the service, the server-side connection is built. The client is automatically authorized if no authorization policy is assigned to the service. Each Zorp service can use an authorization policy to determine whether a client is allowed to access the service. If the authorization is based on the identity of the client, it takes place only after a successful authentication - identity-based authorization can be performed only if the client's identity is known and has been verified. The actual authorization is performed by Zorp, based on the authentication information received from ZAS or extracted from the protocol. Zorp provides the following authorization classes: PermitUser: Authorize listed users. PermitGroup: Authorize users belonging to the specified groups. PermitTime: Authorize connections in a specified time interval. BasicAccessList: Combine other authorization policies into a single rule. PairAuthorization: Authorize only user pairs. NEyesAuthorization: Have another client authorize every connection.
""" import Zorp from Zorp import * from Cache import TimedCache, LockedCache from Exceptions import AAException from AuthDB import getAuthenticationProviderBackend import types, threading, time ## Authentication ##################################################### class AuthenticationPolicy(object): """ A policy determining how the user is authenticated to access the service. Authentication policies determine how the user is authenticated to access the service. The authentication_policy attribute of a service can reference an instance of the AuthenticationPolicy class. A simple authentication policy The following example defines an authentication policy that can be referenced in service definitions. This policy uses inband authentication and references an authentication provider. AuthenticationPolicy(name="demo_authentication_policy", cache=None, authentication=InbandAuthentication(), provider="demo_authentication_provider") To use the authentication policy, include it in the definition of the service: Service(name="office_http_inter", proxy_class=HttpProxy, authentication_policy="demo_authentication_policy", authorization_policy="demo_authorization_policy") Caching authentication decisions The following example defines an authentication policy that caches the authentication decisions for ten minutes (600 seconds). For details on authentication caching, see see ). AuthenticationPolicy(name="demo_authentication_policy", cache=AuthCache(timeout=600, update_stamp=TRUE, service_equiv=TRUE, cleanup_threshold=100), authentication=InbandAuthentication(), provider="demo_authentication_provider") """ def __init__(self, name, provider, authentication, cache = None): """ Constructor to initialize an instance of the AuthenticationPolicy class. name Name identifying the AuthenticationPolicy instance. provider The authentication provider object used in the authentication process. See for details. authentication None The authentication method used in the authentication process. See for details. cache None Caching method used to store authentication results. """ if Globals.authentication_policies.has_key(name): raise ValueError, 'Duplicate AuthenticationPolicy: %s' % name Globals.authentication_policies[name] = self self.name = name self.provider = getAuthenticationProviderBackend(provider) self.authentication = authentication self.cache = cache self.name = name def performAuthentication(self, session): """ """ entity = None if self.cache: entity = self.cache.lookup(session) if not entity: res = self.authentication.performAuth(self.provider, session) else: res = TRUE session.proxy.userAuthenticated(entity[0], entity[1], 'cached') return res def authorized(self, session): """ """ entity = (session.auth_user, session.auth_groups) if self.cache: self.cache.store(session, entity) def unauthorized(self, session): """ """ if self.cache: self.cache.store(session, None) def getAuthenticationPolicy(name): """ """ if name: if Globals.authentication_policies.has_key(name): return Globals.authentication_policies[name] else: log(None, CORE_POLICY, 3, "No such authentication policy; policy='%s'", (name)) return None class AbstractAuthentication(object): """ Class encapsulating the abstract authentication interface. This class encapsulates interfaces for inband and outband authentication procedures. Service definitions should refer to a customized class derived from AbstractAuthentication, or one of the predefined authentication classes, such as InbandAuthentication or ZAAuthentication. authentication_provider AuthenticationProvider instance The authentication provider object used in the authentication process. See for details. """ def __init__(self, authentication_provider=None, auth_cache=None): """ Constructor to initialize an AbstractAuthentication instance. This constructor initializes an instance of the AbstractAuthentication class. authentication_provider None The authentication provider object used in the authentication process. See for details. auth_cache None Caching method used to store authentication results. """ # for compatibility self.cache = auth_cache self.authentication_provider = authentication_provider def performAuth(self, provider, session): """ Function called to initiate authentication before the session is started. This function is called to initiate authentication before a session is started. It should raise AAException if the authentication was not successful. This function is running in the context of the proxy thread, and blocks the proxy until it returns. session SESSION the session object which is to be started """ raise AAException, 'Outband authentication not implemented' class InbandAuthentication(AbstractAuthentication): """ Class encapsulating the inband authentication interface. This class encapsulates inband authentication. Inband authentication is performed by the proxy using the rules of the application-level protocol. Only the authentication methods supported by the particular protocol can be used during inband authentication. Authentication policies can refer to instances of the InbandAuthentication class using the auth parameter. Inband authentication is currently supported only for the Http, Ftp, and Socks proxy classes. """ def __init__(self, authentication_provider=None, auth_cache=None): """ Constructor to initialize an InbandAuthentication instance. This constructor initializes an instance of the InbandAuthentication class. authentication_provider None The authentication provider object to authenticate against auth_cache None The authentication cache object which stores successful authentications. """ super(InbandAuthentication, self).__init__(authentication_provider, auth_cache) def performAuth(self, provider, session): """ """ if not session.proxy.auth_inband_defer: if session.proxy.auth_inband_supported: session.proxy.auth = provider else: raise AAException, 'Inband authentication not supported by the underlying proxy' return TRUE class ServerAuthentication(AbstractAuthentication): """ Class encapsulating the server authentication interface. This class encapsulates server authentication: Zorp authenticates the user based on the response of the server to the user's authentication request. Server authentication is a kind of inband authentication, it is performed within the application protocol, but the target server checks the credentials of the user instead of Zorp. This authentication method is useful when the server can be trusted for authentication purposes, but you need to include an authorization decision in the service definition. """ def __init__(self): """ Constructor to initialize a ServerAuthentication instance. This constructor initializes an instance of the ServerAuthentication class. """ super(ServerAuthentication, self).__init__() def performAuth(self, provider, session): """ """ if session.proxy.auth_server_supported: session.proxy.auth_server = TRUE if provider: session.proxy.auth = provider else: raise AAException, 'Server authentication not supported by the underlying proxy' return TRUE ##################################################### ## AuthCache ##################################################### class AuthCache(object): """ Class encapsulating the authentication cache. This class encapsulates an authentication cache which associates usernames with client IP addresses. The association between a username and an IP address is valid only until the specified timeout. Caching the authentication results means that the users do not need to authenticate themselves for every request: it is assumed that the same user is using the computer within the timeout. E.g.: once authenticated for an HTTP service, the client can browse the web for Timeout period, but has to authenticate again to use FTP. To use a single authorization cache for every service request of a client, set the service_equiv attribute to TRUE. That way Zorp does not make difference between the different services (protocols) used by the client: after a successful authentication the user can use all available services without having to perform another authentication. E.g.: if this option is enabled in the example above, the client does not have to re-authenticate for starting an FTP connection. name String Name of the authentication cache. cache AbstractCache instance TimedCache object where the information is stored. service_equiv Boolean If enabled, then a single authentication of a user applies to every service from that client. """ def __init__(self, name=None, timeout=600, update_stamp=TRUE, service_equiv=FALSE, cleanup_threshold=100): """ Constructor to initialize an instance of the AuthCache class. This constructor initializes and registers an AuthCache instance that can be referenced in authentication policies. name None The name of the authentication cache, this will be used to identify this authentication cache when the obsolete AuthPolicy construct is used. Setting this value is not required if AuthenticationPolicy is used. timeout 600 Timeout while an authentication is assumed to be valid. update_stamp TRUE If set to TRUE, then cached authentications increase the validity period of the authentication cache. Otherwise, the authentication cache expires according to the timeout value set in . service_equiv FALSE If enabled, then a single authentication of a user applies to every service from that client. cleanup_threshold 100 When the number of entries in the cache reaches the value of cleanup_threshold, old entries are automatically deleted. """ if name: self.name = name cache_name = 'authcache(%s)' % self.name else: cache_name = 'authcache(noname)' self.cache = LockedCache(TimedCache(cache_name, timeout, update_stamp, cleanup_threshold)) self.service_equiv = service_equiv if name: if Globals.auth_caches.has_key(name): raise ValueError, "Duplicate AuthCache name: %s" % name Globals.auth_caches[name] = self def makeKey(self, session): """ """ if self.service_equiv: return session.client_address.ip_s else: return (session.client_address.ip_s, session.service.name) def lookup(self, session): """ """ return self.cache.lookup(self.makeKey(session)) def store(self, session, entity): """ """ return self.cache.store(self.makeKey(session), entity) def getAuthCacheByName(name): """ """ if name: if Globals.auth_caches.has_key(name): return Globals.auth_caches[name] else: log(None, CORE_POLICY, 3, "No such authentication cache; cache='%s'", (name)) return None ##################################################### ## Authorization ##################################################### ##################################################### ## Compatibility ##################################################### class AuthPolicy(object): """ Class encapsulating the authentication and authorization (AA) policy. This class encapsulates authentication and authorization policy, which can be associated with Zorp services. This class is obsolete, please use AuthenticationPolicy and AuthorizationPolicy instead. A user will not be able to use a given service if it cannot fulfill the authentication and authorization requirements this class poses. Both an authentication (for example SatyrAuthentication) and an authorization (for example BasicAccessList) can be set, tough both of them is optional. name Name of the authentication policy, this value is used when associating authentication policies to services. authentication Authentication object which performs authentication and stores its result to make it possible to perform authorization. --> """ def __init__(self, name, authentication=None, authorization=None, auth_cache=None): """ Constructor to initialize an AuthPolicy instance. This constructor creates a new AuthPolicy instance which can be associated with a Zorp service to perform authentication and authorization. name Name of the authentication policy, this value is used when associating authentication policies to services. authentication None Authentication object which performs authentication and stores its result to make it possible to perform authorization. --> """ self.name = name self.authentication = authentication self.auth_cache = auth_cache # FIXME: this is a hack for compatibility and might be removed as soon # as SatyrAuthentication.cache is removed. if not auth_cache and hasattr(authentication, 'cache'): self.auth_cache = authentication.cache if type(self.auth_cache) == types.StringType: self.auth_cache = getAuthCacheByName(self.auth_cache) if name: if Globals.auth_policies.has_key(name): raise ValueError, "Duplicate AuthPolicy name: %s" % name Globals.auth_policies[name] = self def getAuthenticationPolicy(self): if Globals.authentication_policies.has_key('__%s-authentication' % self.name): return Globals.authentication_policies['__%s-authentication' % self.name] else: return AuthenticationPolicy('__%s-authentication' % self.name, self.authentication.authentication_provider, self.authentication, self.auth_cache) def getAuthorizationPolicy(self): if self.authorization: if Globals.authorization_policies.has_key('__%s-authorization' % self.name): Globals.authorization_policies['__%s-authorization' % self.name] else: return AuthorizationPolicy('__%s-authorization' % self.name, self.authorization) return None def getAuthPolicyObsolete(name): """""" if name: if Globals.auth_policies.has_key(name): return Globals.auth_policies[name] else: log(None, CORE_POLICY, 3, "No such AuthPolicy; policy='%s'", (name)) return None zorp-3.9.5/pylib/Zorp/AuthDB.py000066400000000000000000000327351172670260400162730ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the authentication databases. This module contains classes related to authentication databases. Together with the Auth module it implements the Authentication and Authorization framework of Zorp. See and for details. """ import Zorp from Zorp import * Z_AUTH_UNKNOWN = 0 Z_AUTH_GETMETHODS = 1 Z_AUTH_METHODS = 2 Z_AUTH_SETMETHOD = 3 Z_AUTH_REQUEST = 4 Z_AUTH_RESPONSE = 5 Z_AUTH_ACCEPT = 6 Z_AUTH_ABORT = 7 Z_AUTH_REJECT = 8 class AuthenticationProvider(object): """ A database-independent class used by Zorp to connect to an authentication backend. The authentication provider is an intermediate layer that mediates between Zorp and the authentication backend (e.g., a user database) during connection authentication - Zorp itself does not directly communicate with the database. A sample authentication provider The following example defines an authentication provider that uses the ZAS2AuthenticationBackend backend. AuthenticationProvider(name="demo_authentication_provider", backend=ZAS2AuthenticationBackend(serveraddr=SockAddrInet('192.168.10.10', 1317), use_ssl=TRUE, ssl_verify_depth=3, pki_cert=("/etc/key.d/ZAS_certificate/cert.pem", "/etc/key.d/ZAS_certificate/key.pem"), pki_ca=("/etc/ca.d/groups/demo_trusted_group/certs/", "/etc/ca.d/groups/demo_trusted_group/crls/"))) """ def __init__(self, name, backend): """ Constructor to initialize an AbstractAuthorizationBackend instance. This constructor initializes an AbstractAuthorizationBackend instance. name Name of the ZAS instance. backend Type of the database backend used by the ZAS instance. """ if Globals.authentication_providers.has_key(name): raise ValueError, "Duplicate authorization provider: %s" % name Globals.authentication_providers[name] = self self.name = name self.backend = backend def getAuthenticationProviderBackend(name): """ """ if name: if Globals.authentication_providers.has_key(name): return Globals.authentication_providers[name].backend else: log(None, CORE_POLICY, 3, "No such authentication provider; provider='%s'", (name)) return None class AbstractAuthenticationBackend(object): """ Class encapsulating the abstract authentication backend like ZAS. This is an abstract class to encapsulate an authentication backend, which is responsible for checking authentication credentials against a backend database. In actual configurations, use one of the derived classes like ZAS2AuthenticationBackend. The interface defined here is used by various authentication methods like ZAAuthentication and InbandAuthentication. """ def __init__(self): """ Constructor to initialize an AbstractAuthorizationBackend instance. This constructor initializes an AbstractAuthorizationBackend instance. """ pass def startSession(self, session_id, session): """ Method to be called when an authentication session starts. This method is called when an authentication session identified by 'session_id' starts. 'session_id' can be used to associate data with this session, as each subsequent calls to AbstractAuthorization methods will get this value. session_id session identifier represented as a string """ pass def stopSession(self, session_id): """ Method to be called when an authentication session ends. This method is called when an authentication session is ended. It's a placeholder for freeing up any resources associated to a given session. """ pass def getMethods(self, session_id, entity): """ Function returning the allowed set of methods. This function calculates and returns the set of allowed methods a user is allowed to authenticate with. We return an empty set here, overridden methods should return something more interesting. Returns return a tuple. First value is Z_AUTH_*, the second is a array of applicable methods. (if any) session_id authentication session id entity username """ return None def setMethod(self, session_id, method): """ Function to set the authentication Method. This function should return a challenge for a given entity using the given method, or None if challenge is not applicable for the given method. Returns return a tuple. First value is one of Z_AUTH*, second value is a string containing the challenge, or None if not applicable session_id authentication session id entity username method authentication method """ raise NotImplementedError def converse(self, session_id, credentials): """ Function checking the presented credentials of an entity. This function is called to check the credentials presented by the client for validity. It should return either TRUE, if the credentials for the given challenge method username are valid. Returns return a tuple. First value is one of Z_AUTH_*, second is depending on the first. session_id authentication session id entity username challenge a previously issued challenge (might be None or an empty string) credentials response for the given challenge """ raise NotImplementedError zorp-3.9.5/pylib/Zorp/Cache.py000066400000000000000000000204031172670260400161540ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining general cache related classes and functions. Caching is used throughout the policy layer to improve performance. This module includes a couple of general caching classes used by various parts of the policy code. """ from Zorp import * import time, threading class AbstractCache(object): """ """ def __init__(self, name): """ """ self.name = name def store(self, key, value): """ Stores a value in the cache identified by 'key'. Deletes the item if value is None. """ pass def lookup(self, key): """ Looks up a value identified by 'key', returns None if not found. """ pass def clear(self): """ """ pass class ShiftCache(AbstractCache): """ """ def __init__(self, name, shift_threshold): """ """ super(ShiftCache, self).__init__(name) self.cache = {} self.old_cache = {} self.shift_threshold = shift_threshold def store(self, key, value): """ """ if len(self.cache) > self.shift_threshold: ## LOG ## # This message indicates that the cache size(threshold) is reached, and cache is shifted. # @see: Cache.ShiftCache ## log(None, CORE_MESSAGE, 3, "Cache over shift-threshold, shifting; cache='%s', threshold='%d'", (self.name, self.shift_threshold,)) self.old_cache = self.cache self.cache = {} if value: self.cache[key] = value else: try: del self.cache[key] except KeyError: pass try: del self.old_cache[key] except KeyError: pass def lookup(self, key): """ """ val = None try: return self.cache[key] except KeyError: pass try: val = self.old_cache[key] self.cache[key] = val del self.old_cache[key] except KeyError: pass return val def clear(self): """ """ self.cache = {} self.old_cache = {} class TimedCache(AbstractCache): """ """ def __init__(self, name, timeout, update_stamp=TRUE, cleanup_threshold=100): """ """ super(TimedCache, self).__init__(name) self.timeout = timeout self.update_stamp = update_stamp self.cleanup_threshold = cleanup_threshold self.cache = {} def cleanup(self): """ """ now = time.time() for x in self.cache.keys(): if now - self.cache[x][0] > self.timeout: del self.cache[x] def lookup(self, key): """ """ if len(self.cache) > self.cleanup_threshold: self.cleanup() if self.cache.has_key(key): entry = self.cache[key] if time.time() - entry[0] > self.timeout: del self.cache[key] else: if self.update_stamp: entry[0] = time.time() return entry[1] return None def store(self, key, value): """ """ if value: if not self.cache.has_key(key) or self.cache[key][1] != value: # only update if value is different self.cache[key] = [time.time(), value] else: try: del self.cache[key] except KeyError: pass def clear(self): """ """ self.cache = {} class LockedCache(AbstractCache): """ """ def __init__(self, child_cache): self.child_cache = child_cache self.lock = threading.Lock() def store(self, key, value): """ """ try: self.lock.acquire() return self.child_cache.store(key, value) finally: self.lock.release() def lookup(self, key): """ """ try: self.lock.acquire() return self.child_cache.lookup(key) finally: self.lock.release() def clear(self): """ """ try: self.lock.acquire() return self.child_cache.clear() finally: self.lock.release() zorp-3.9.5/pylib/Zorp/Chainer.py000066400000000000000000001224271172670260400165330ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Chainer module defines the classes required to connect to the target servers. Chainers establish a TCP or UDP connection between a proxy and a selected destination. The destination is usually a server, but the SideStackChainer connects an additional proxy before connecting the server.
Selecting the network protocol The client-side and the server-side connections can use different networking protocols if needed. The protocol attribute of the chainer classes determines the network protocol used in the server-side connection. By default, Zorp uses the same protocol in both connections. The following options are available: The network protocol used in the server-side connection NameDescription ZD_PROTO_AUTO Use the protocol that is used on the client side. ZD_PROTO_TCP Use the TCP protocol on the server side. ZD_PROTO_UDP Use the UDP protocol on the server side.
""" from Zorp import * from Session import MasterSession from Stream import Stream from Session import StackedSession from SockAddr import SockAddrInet from NAT import NAT_SNAT, NAT_DNAT from Cache import TimedCache from Exceptions import DACException import types class AbstractChainer(object): """ Class encapsulating the abstract chainer. AbstractChainer implements an abstract chainer that establishes a connection between the parent proxy and the selected destination. This class serves as a starting point for customized chainer classes, but is itself not directly usable. Service definitions should refer to a customized class derived from AbstractChainer, or one of the predefined chainer classes, such as ConnectChainer or FailoverChainer. """ def __init__(self): """ """ pass def chainParent(self, session): """ Function to be called when a proxy wants to connect to its parent. This function is called to actually perform chaining to the parent. session SESSION session we belong to """ raise NotImplementedError class ConnectChainer(AbstractChainer): """ Class to establish the server-side TCP/IP connection. ConnectChainer is the default chainer class based on AbstractChainer. This class establishes a TCP or UDP connection between the proxy and the selected destination address. ConnectChainer is used by default if no other chainer class is specified in the service definition. ConnectChainer attempts to connect only a single destination address: if the connection establishment procedure selects multiple target servers (e.g., a DNSResolver with the multi=TRUE parameter or a DirectedRouter with multiple addresses), ConnectChainer will use the first address and ignore all other addresses. Use FailoverChainer to select from the destination from multiple addresses in a failover fashion, and RoundRobinChainer to distribute connections in a roundrobin fashion. A sample ConnectChainer The following service uses a ConnectChainer that uses the UDP protocol on the server side. Service(name="demo_service", proxy_class=HttpProxy, chainer=ConnectChainer(protocol=ZD_PROTO_UDP), router=TransparentRouter(overrideable=FALSE, forge_addr=FALSE)) """ def __init__(self, protocol=ZD_PROTO_AUTO, timeout_connect=None): """ Constructor to initialize an instance of the ConnectChainer class. This constructor creates a new ConnectChainer instance which can be associated with a Service. protocol ZD_PROTO_AUTO Optional parameter that specifies the network protocol used in the connection protocol. By default, the server-side communication uses the same protocol that is used on the client side. See for details. timeout_connect 30000 Specifies connection timeout to be used when connecting to the target server. """ super(ConnectChainer, self).__init__() self.protocol = protocol if not timeout_connect: self.timeout_connect = config.options.timeout_server_connect else: self.timeout_connect = timeout_connect def establishConnection(self, session, local, remote): """ Function to actually establish a connection. Internal function to establish a connection with the given local and remote addresses. It is used by derived chainer classes after finding out the destination address to connect to. This function performs access control checks. Returns The stream of the connection to the server session SESSION session we belong to local bind address remote host to connect to protocol protocol to connect to """ protocol = self.protocol if protocol == 0: protocol = session.protocol if remote.port == 0: remote.port = session.client_local.port session.setServerAddress(remote) if session.isServerPermitted() == ZV_ACCEPT: #remote.options = session.client_address.options try: conn = Attach(session.proxy, protocol, local, remote, tos=session.proxy.server_local_tos, local_loose=session.target_local_loose, timeout=self.timeout_connect, local_random=session.target_local_random) session.server_stream = conn.start() session.server_local = conn.local except IOError: session.server_stream = None if session.server_stream == None: ## LOG ## # This message indicates that the connection to the server failed. ## log(session.session_id, CORE_SESSION, 3, "Server connection failure; server_address='%s', server_zone='%s', server_local='%s', server_protocol='%s'", (session.server_address, session.server_zone, session.server_local, ZD_PROTO_NAME[protocol])) else: session.server_stream.name = session.session_id + "/server" session.server_stream.keepalive = session.service.keepalive & Z_KEEPALIVE_SERVER ## LOG ## # This message indicates that the connection to the server succeeded. ## log(session.session_id, CORE_SESSION, 3, "Server connection established; server_fd='%d', server_address='%s', server_zone='%s', server_local='%s', server_protocol='%s'", (session.server_stream.fd, session.server_address, session.server_zone, session.server_local, ZD_PROTO_NAME[protocol])) szigEvent(Z_SZIG_CONNECTION_PROPS, (Z_SZIG_TYPE_CONNECTION_PROPS, (session.service.name, session.instance_id, 0, 0, { 'server_address': str(session.server_address), 'server_local': str(session.server_local), 'server_zone': session.server_zone.getName(), } ))) return session.server_stream raise DACException('Server connection is not permitted') def getNextTarget(self, session): """ """ return (session.target_local, session.target_address[0]) def connectTarget(self, session, target_local, target_remote): """ """ if session.service.snat_policy: local = session.service.snat_policy.performTranslation(session, (target_local, target_remote), NAT_SNAT) else: local = target_local if session.service.dnat_policy: remote = session.service.dnat_policy.performTranslation(session, (target_local, target_remote), NAT_DNAT) else: remote = target_remote return self.establishConnection(session, local, remote) def chainParent(self, session): """ Function to perform connection establishment. This function is called by the underlying proxy implementation to actually connect to the server-endpoint of the session. The destination address is 'session.server_address' (which is previously set by the Router used for the service, and optionally overridden by the Proxy). The local address to bind to is determined with the help of a NAT object if one is provided, or allocated dynamically by the kernel. session session we belong to """ try: (target_local, target_remote) = self.getNextTarget(session) except ValueError: target_local = None target_remote = None if target_remote == None: ## LOG ## # This message indicates that the connection to the # server can not be established, because no server # address is set. ## log(session.session_id, CORE_SESSION, 3, "Server connection failure, no destination;") return None return self.connectTarget(session, target_local, target_remote) class MultiTargetChainer(ConnectChainer): """ Class encapsulating connection establishment with multiple target addresses. This class encapsulates a real TCP/IP connection establishment, and is used when a top-level proxy wants to perform chaining. In addition to ConnectChainer, this class adds the capability to perform stateless, simple load balance server connections among a set of IP addresses. The same mechanism is used to set multiple server addresses as with a single destination address: the Router class sets a list of IP addresses in the session.target_address attribute. connection_count the number of connections established using this chainer """ def __init__(self, protocol=ZD_PROTO_AUTO, timeout_connect=None): """ Constructor to initialize a MultiTargetChainer instance. This constructor initializes a MultiTargetChainer class by filling arguments with appropriate values and calling the inherited constructor. self this instance protocol ZD_PROTO_AUTO Optional, specifies connection protocol (either ZD_PROTO_TCP or ZD_PROTO_UDP), when not specified defaults to the same protocol as was used on the client side. timeout_connect 30000 Specifies connection timeout to be used when connecting to the target server. """ super(MultiTargetChainer, self).__init__(protocol, timeout_connect) self.connection_count = 0 def restart(self, session): """ """ session.chainer_restart = TRUE def getFirstTargetIndex(self, session): """ """ return self.connection_count % len(session.target_address) def getNextTarget(self, session): """ """ if not hasattr(session, 'chainer_first_attempt') or session.chainer_restart: session.chainer_first_attempt = self.getFirstTargetIndex(session) session.chainer_current_host = session.chainer_first_attempt session.chainer_restart = FALSE else: # we made our complete round on targets if session.chainer_first_attempt == session.chainer_current_host: return (None, None) target_remote = session.target_address[session.chainer_current_host] session.chainer_current_host = (session.chainer_current_host + 1) % len(session.target_address) self.connection_count = self.connection_count + 1 return (session.target_local, target_remote) def disableTarget(self, session, target_local, target_remote): """ """ pass def chainParent(self, session): """ Overridden function to perform connection establishment. This function is called by the actual Proxy implementation to actually connect to the server-endpoint of the session. The destination address is 'session.server_address' (which is previously set by the Router used for the service, and optionally overridden by the Proxy). The local address to bind to is determined with the help of a NAT object if one is provided, or allocated dynamically by the kernel. The failover capability of FailoverChainer is implemented here. session SESSION session we belong to """ stream = None (target_local, target_remote) = self.getNextTarget(session) while target_remote != None: stream = self.connectTarget(session, target_local, target_remote) if not stream: self.disableTarget(session, target_local, target_remote) (target_local, target_remote) = self.getNextTarget(session) else: self.restart(session) return stream class StateBasedChainer(MultiTargetChainer): """ Class encapsulating connection establishment with multiple target addresses and keeping down state between connects. This class encapsulates a real TCP/IP connection establishment, and is used when a top-level proxy wants to perform chaining. In addition to ConnectChainer, this class adds the capability to perform stateful, load balance server connections among a set of IP addresses. Both the FailoverChainer and RoundRobinChainer classes are derived from StateBasedChainer. state Down state of target hosts. """ def __init__(self, protocol=ZD_PROTO_AUTO, timeout_connect=None, timeout_state=None): """ Constructor to initialize a StateBasedChainer instance. This constructor initializes a StateBasedChainer class by filling arguments with appropriate values and calling the inherited constructor. protocol ZD_PROTO_AUTO Optional, specifies connection protocol ( ZD_PROTO_TCP or ZD_PROTO_UDP ), when not specified it defaults to the same protocol used on the client side. timeout_connect 30000 Specifies connection timeout to be used when connecting to the target server. timeout_state 60000 The down state of remote hosts is kept for this interval in miliseconds. """ super(StateBasedChainer, self).__init__(protocol, timeout_connect) if not timeout_state: timeout_state = 60000 self.state = TimedCache('chainer-state', int((timeout_state + 999) / 1000), update_stamp=FALSE) def getNextTarget(self, session): """ """ while 1: (target_local, target_remote) = super(StateBasedChainer, self).getNextTarget(session) if not target_remote: # we enumerated all targets try: session.chainer_targets_enumerated = session.chainer_targets_enumerated + 1 except AttributeError: session.chainer_targets_enumerated = 1 if not self.state or session.chainer_targets_enumerated == 2: # we enumerated all our targets twice, once # with state held, and then all state # cleared, we were not successful, terminate # target iteration log(None, CORE_MESSAGE, 4, "All destinations are down for two full iterations, giving up;") return (None, None) ## LOG ## # This message reports that the remote end is down and Zorp stores the # down state of the remote end, so Zorp wont try to connect to it within the # timeout latter. ## log(None, CORE_MESSAGE, 4, "All destinations are down, clearing cache and trying again;") # we enumerated all targets, and all of them were # down, clear our state and try once more self.state.clear() self.restart(session) continue is_host_down = self.state.lookup(target_remote.ip_s) if not is_host_down: return (target_local, target_remote) else: ## LOG ## # This message reports that the remote end is down, but Zorp does not store the # down state of the remote end, so Zorp will try to connect to it next time. ## log(session.session_id, CORE_MESSAGE, 4, "Destination is down, skipping; remote='%s'", (target_remote,)) def disableTarget(self, session, target_local, target_remote): """ """ ## LOG ## # This message reports that the remote end is down and Zorp stores the # down state of the remote end, so Zorp wont try to connect to it within the # timeout latter. ## log(session.session_id, CORE_MESSAGE, 4, "Destination is down, keeping state; remote='%s'", (target_remote,)) self.state.store(target_remote.ip_s, 1) class FailoverChainer(StateBasedChainer): """ Class encapsulating the connection establishment with multiple target addresses and keeping down state between connects. FailoverChainer prefers connecting to target hosts in the order they were specified. This class is based on the StateBasedChainer class and encapsulates a real TCP/IP connection establishment, and is used when a top-level proxy wants to perform chaining. In addition to ConnectChainer this class adds the capability to perform stateful, failover HA functionality across a set of IP addresses. Use FailoverChainer if you want to connect to the servers in a predefined order: i.e., connect to the first server, and only connect to the second if the first server is unavailable. If you want to distribute connections between the servers (i.e., direct every new connection to a different server to balance the load) use RoundRobinChainer . A DirectedRouter using FailoverChainer The following service definition uses a DirectedRouter class with two possible destination addresses. Zorp uses these destinations in a failover fashion, targeting the second address only if the first one is unaccessible. Service(name="intra_HTTP_inter", router=DirectedRouter(dest_addr=(SockAddrInet('192.168.55.55', 8080), SockAddrInet('192.168.55.56', 8080)), forge_addr=FALSE, forge_port=Z_PORT_ANY, overrideable=FALSE), chainer=FailoverChainer(protocol=ZD_PROTO_AUTO, timeout_state=60000, timeout_connect=30000), max_instances=0, proxy_class=HttpProxy,) state down state of target hosts """ def __init__(self, protocol=ZD_PROTO_AUTO, timeout=0, timeout_state=None, timeout_connect=None, round_robin=FALSE): """ Constructor to initialize a FailoverChainer instance. This constructor initializes a FailoverChainer class by filling arguments with appropriate values and calling the inherited constructor. protocol ZD_PROTO_AUTO Optional, specifies connection protocol ( ZD_PROTO_TCP or ZD_PROTO_UDP ), when not specified it defaults to the protocol used on the client side. timeout_state 60000 The down state of remote hosts is kept for this interval in milliseconds. timeout_connect 30000 Specifies connection timeout to be used when connecting to the target server. timeout 0 Obsolete alias for timeout_state, specified in seconds. round_robin FALSE Obsolete argument to direct FailoverChainer to behave like a RoundRobinChainer. """ if timeout: timeout_state = timeout * 1000 super(FailoverChainer, self).__init__(protocol, timeout_connect, timeout_state) self.round_robin = round_robin def getFirstTargetIndex(self, session): """ """ if self.round_robin: return super(FailoverChainer, self).getFirstTargetIndex(session) return 0 class RoundRobinChainer(StateBasedChainer): """ Class encapsulating the connection establishment with multiple target addresses and keeping down state between connects. This class is based on the StateBasedChainer class and encapsulates a real TCP/IP connection establishment, and is used when a top-level proxy wants to perform chaining. In addition to ConnectChainer this class adds the capability to perform stateful, load balance server connections among a set of IP addresses. A DirectedRouter using RoundRobinChainer The following service definition uses a RoundRobinChainer class with two possible destination addresses. Zorp uses these destinations in a roundrobin fashion, alternating between the two destinations. Service(name="intra_HTTP_inter", router=DirectedRouter(dest_addr=(SockAddrInet('192.168.55.55', 8080), SockAddrInet('192.168.55.56', 8080)), forge_addr=FALSE, forge_port=Z_PORT_ANY, overrideable=FALSE), chainer=RoundRobinChainer(protocol=ZD_PROTO_AUTO, timeout_state=60000, timeout_connect=30000), max_instances=0, proxy_class=HttpProxy) """ pass class SideStackChainer(AbstractChainer): """ Class to pass the traffic to another proxy. This class encapsulates a special chainer. Instead of establishing a connection to a server, it creates a new proxy instance and connects the server side of the current (parent) proxy to the client side of the new (child) proxy. The right_class parameter specifies the child proxy. It is possible to stack multiple proxies side-by-side. The final step of sidestacking is always to specify a regular chainer via the right_chainer parameter that connects the last proxy to the destination server. Proxy sidestacking is useful for example to create one-sided SSL connections. See the tutorials of the BalaBit Documentation Page available at http://www.balabit.com/support/documentation/ for details. right_class The proxy class to connect to the parent proxy. Both built-in and customized classes can be used. right_chainer The chainer used to connect to the destination of the side-stacked proxy class set in the right_class attribute. """ def __init__(self, right_class, right_chainer = None): """ Constructor to initialize an instance of the SideStackChainer class. This constructor creates a new FailoverChainer instance which can be associated with a Service. right_class The proxy class to connect to the parent proxy. Both built-in or customized classes can be used. right_chainer None The chainer used to connect to the destionation of the side-stacked proxy class set in the right_class attribute. """ super(SideStackChainer, self).__init__() self.right_class = right_class if right_chainer == None: right_chainer = ConnectChainer() self.right_chainer = right_chainer def chainParent(self, session): """ Overridden function to perform chaining. This function is called by a Proxy instance to establish its server side connection. Instead of connecting to a server this chainer creates another proxy instance and connects this new proxy with the current one. session SESSION session we belong to """ try: streams = streamPair(AF_UNIX, SOCK_STREAM) except IOError: ## LOG ## # This message indicates that side stacking failed, because Zorp was unable to create a socketPair. # It is likely that there is now resource available. Try increase fd limits. ## log(session.session_id, CORE_SESSION, 3, "Side stacking failed, socketPair failed;") return None try: # convert our tuple to an array, to make it possible # to modify items streams = [streams[0], streams[1]] ss = None session.server_stream = streams[0] session.server_stream.name = session.owner.session_id + "/leftside" ss = StackedSession(session, self.right_chainer) streams[0] = None ss.client_stream = streams[1] ss.client_stream.name = ss.session_id + "/rightside" ss.server_stream = None streams[1] = None ## LOG ## # This message indicates that side stacking was successful. ## log(session.session_id, CORE_SESSION, 4, "Side-stacking proxy instance; server_fd='%d', client_fd='%d', proxy_class='%s'", (session.server_stream.fd, ss.client_stream.fd, self.right_class.__name__)) proxy = self.right_class(ss) if ProxyGroup(1).start(proxy): return ss.client_stream else: raise RuntimeError, "Error starting proxy in group" except: ## LOG ## # This message indicates that side stacking failed. ## log(session.session_id, CORE_ERROR, 3, "Side-stacking failed; proxy_class='%s'", (self.right_class.__name__)) if ss: ss.destroy() if (streams[0] != None): streams[0].close() if (streams[1] != None): streams[1].close() zorp-3.9.5/pylib/Zorp/Config.py000066400000000000000000000144601172670260400163640ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Config module defines global options of Zorp. This module defines global options of Zorp. For a detailed description of the options, see . """ import new, sys TRUE = 1 FALSE = 0 config = sys.modules[__name__] def addConfigContainer(cont): """ Create a container for global configuration variables. """ setattr(config, cont, new.module(cont)) addConfigContainer('blob') # The directory where blobs are swapped out config.blob.temp_directory = '/var/lib/zorp/tmp/' # Maximum disk usage (1G) config.blob.max_disk_usage = 1024*0x100000 # Maximum memory usage (256M) config.blob.max_mem_usage = 256*0x100000 # Low water mark for blob swapout, it stops when reaching this amount in memory config.blob.lowat = 96*0x100000 # High water mark for blob swapout, swapout starts when having this amount of memory used config.blob.hiwat = 128*0x100000 # The maximum size for blobs that are never swapped. config.blob.noswap_max = 16384 addConfigContainer('audit') # Whether each session has a separate audit trail file. config.audit.per_session = FALSE ## Session level options, controlling what to do when auditing is enabled ## for a session. # whether to write records to audit trail file (if you disable this and # config.audit.ids, then initializing the audit trail will fail) config.audit.audit = TRUE # Whether audit trail encryption is enabled config.audit.encrypt = FALSE # Whether to compress audit trails config.audit.compress = TRUE # Sign the digest record config.audit.sign = FALSE # Timestamp the digest record config.audit.timestamp = FALSE config.audit.ids = FALSE ## Compression options # The compression level for audit trail files config.audit.compress_level = 1 ## Encryption options # List of X.509 PEM certificates. to encrypt the audit trail with config.audit.encrypt_certificate_list = None # File names which contain an X.509 PEM certificate to encrypt the audit # trail file, overrides the setting for config.audit.encrypt_certificate_list #config.audit.encrypt_certificate_list_file = [ ["", "" ], ] # by default empty: config.audit.encrypt_certificate_list_file = None # X.509 PEM certificate to encrypt the audit trail file for. Fallback if config.audit.encrypt_certifiace_list is empty config.audit.encrypt_certificate = None # File name which contains an X.509 PEM certificate to encrypt the audit # trail file, overrides the setting for config.audit.encrypt_certificate config.audit.encrypt_certificate_file = None ## Sign related options # Seconds between audit trail digest record is written # Optionally the digest can be timestamped by a server and sign by an RSA key # This whole record is the digital sign of the trail config.audit.sign_interval = 30 # RSA or DSA private key to sign the digest calculated for the sign record config.audit.sign_private_key = None # Certificate to sign the digest calculated for the sign record config.audit.sign_certificate = None # File of private key to sign the digest calculated for the sign record config.audit.sign_private_key_file = None # File of the certificate to sign the digest calculated for the sign record config.audit.sign_certificate_file = None ## Timestamping options # Timestamping URL for the digest record config.audit.timestamp_url = "" # Policy of the timestamping server (ASN1) # in form of "1.2.4.3.124.7" config.audit.timestamp_policy = "" # Max length of the timestamp field of the digest record config.audit.timestamp_length = 3072 ## IDS options # Interface to use towards the IDS sensor config.audit.ids_interface = "" # IDS source MAC address config.audit.ids_src_mac = "" # IDS destination MAC address config.audit.ids_dst_mac = "" ## Misc parameters # Audit trail files are reopened (and a new one started) when they reach this number config.audit.reopen_size_threshold = 2000000000L # Audit trail files are reopened after this amount of time has elapsed config.audit.reopen_time_threshold = 28800 # Rate of filling the bucket in byte/sec config.audit.rate_limit = 2*1024*1024 # Interval between two notifications, if bucket is empty, in seconds config.audit.rate_notification_interval = 300 # Maximum size of audit trail files in bytes config.audit.write_size_max = 50*1024*1024 # Terminate proxy if cannot write audit trail (max size exceeded) config.audit.terminate_on_max_size = FALSE addConfigContainer('options') # The timeout used when establishing server side connection. config.options.timeout_server_connect = 30000 # The default language used for user messages in various proxies. config.options.language = "en" # Zone and CSZoneDispatcher shift cache parameter config.options.zone_dispatcher_shift_threshold = 1000 # Zone lookup shift cache parameter config.options.zone_cache_shift_threshold = 1000 # Inbound DAC shift cache parameter config.options.inbound_service_cache_threshold = 1000 # Outbound DAC shift cache parameter config.options.outbound_service_cache_threshold = 1000 # DSCP -> thread priority mapping config.options.dscp_prio_mapping = {} # KZorp enabled or not. If KZorp is not present in the kernel and this is # enabled, Zorp startup/shutdown/reload will be delayed by about 5sec config.options.kzorp_enabled = TRUE zorp-3.9.5/pylib/Zorp/Core.py000066400000000000000000000051351172670260400160460ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the Core. This module imports all public Zorp interfaces and makes it easy to use those from the user policy file by simply importing all symbols from Zorp.Core. """ import new import socket from Zorp import * import Zorp from Zone import Zone, InetZone from Service import Service, PFService from SockAddr import SockAddrInet, SockAddrInetHostname, SockAddrInetRange, SockAddrInet6, SockAddrUnix from Router import TransparentRouter, DirectedRouter, InbandRouter from Chainer import ConnectChainer, MultiTargetChainer, StateBasedChainer, RoundRobinChainer, FailoverChainer, SideStackChainer from Subnet import InetSubnet, InetDomain, Inet6Subnet from Listener import Listener, ZoneListener, CSZoneListener from Dispatch import Dispatcher, ZoneDispatcher, CSZoneDispatcher, RuleDispatcher from Rule import PortRange, Rule from NAT import NATPolicy, ForgeClientSourceNAT, StaticNAT, OneToOneNAT, OneToOneMultiNAT, RandomNAT, HashNAT, GeneralNAT from Proxy import proxyLog from Auth import InbandAuthentication, AuthCache, AuthPolicy, AuthenticationPolicy from Stack import StackingProvider, RemoteStackingBackend from Matcher import MatcherPolicy, AbstractMatcher, RegexpMatcher, RegexpFileMatcher, CombineMatcher, DNSMatcher, WindowsUpdateMatcher, SmtpInvalidRecipientMatcher from Resolver import DNSResolver, HashResolver, ResolverPolicy from Encryption import EncryptionPolicy, TLSEncryption # conntrack support try: from Receiver import Receiver, ZoneReceiver, CSZoneReceiver except: pass zorp-3.9.5/pylib/Zorp/Dispatch.py000066400000000000000000001632661172670260400167270ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Dispatch module defines the classes that accept incoming connections. Dispatchers bind to a specific IP address and port of the Zorp firewall and wait for incoming connection requests. For each accepted connection, the Dispatcher creates a new service instance to handle the traffic arriving in the connection. Earlier Zorp versions used different classes to handle TCP and UDP connections (Listeners and Receivers, respectively). These classes have been merged into the Dispatcher module. For each accepted connection, the Dispatcher creates a new service instance to handle the traffic arriving in the connection. The service started by the dispatcher depends on the type of the dispatcher: Dispatchers start the same service for every connection. CSZoneDispatchers start different services based on the zones the client and the destination server belong to. Only one dispatcher can bind to an IP address/port pair.
Zone-based service selection Dispatchers can start only a predefined service. Use CSZonedDispatchers to start different services for different connections. CSZoneDispatchers assign different services to different client-server zone pairs. Define the zones and the related services in the services parameter. The * wildcard matches all client or server zones. The server zone may be modified by the proxy, the router, the chainer, or the NAT policy used in the service. To select the service, CSZoneDispatcher determines the server zone from the original destination IP address of the incoming client request. Similarly, the client zone is determined from the source IP address of the original client request. To accept connections from the child zones of the selected client zones, set the follow_parent attribute to TRUE. Otherwise, the dispatcher accepts traffic only from the client zones explicitly listed in the services attribute of the dispatcher.
ZD_PRI_LISTEN ZD_PRI_NORMAL ZD_PRI_RELATED
""" from Zorp import * from Zone import Zone from Session import MasterSession from Cache import ShiftCache from SockAddr import SockAddrInet, SockAddrInet6 from Subnet import Subnet from Util import * from Exceptions import * from traceback import print_exc import Zorp, SockAddr import collections, sys import kznf.kznfnetlink as kznf import socket listen_hook = None unlisten_hook = None dispatch_id = 0 ZD_PRI_LISTEN = 100 ZD_PRI_NORMAL = 0 ZD_PRI_RELATED = -100 """ """ def convertSockAddrToDB(sa, protocol): """ """ if isinstance(sa, collections.Sequence): return map(lambda x: convertSockAddrToDB(x), sa) if isinstance(sa, Zorp.SockAddrType): if protocol == ZD_PROTO_AUTO: raise ValueError, "No preferred protocol specified" return DBSockAddr(sa, protocol=protocol) else: if sa.protocol: if sa.protocol != protocol: raise ValueError, "Protocol number mismatch (%d != %d)" % (sa.protocol, protocol) else: sa.protocol = protocol return sa def convertDBProtoToIPProto(dbproto): """ """ if dbproto == ZD_PROTO_TCP: return socket.IPPROTO_TCP elif dbproto == ZD_PROTO_UDP: return socket.IPPROTO_UDP raise ValueError, "Unknown dispatch bind protocol" def convertIPProtoToDBProto(ipproto): """ """ if ipproto == socket.IPPROTO_TCP: return ZD_PROTO_TCP elif ipproto == socket.IPPROTO_UDP: return ZD_PROTO_UDP raise ValueError, "IP protocol not representable in DBProto; protocol='%d'" % (ipproto,) class BaseDispatch(object): """ Class encapsulating the abstract Dispatch interface. """ def __init__(self, session_id, bindto=None, **kw): """ """ global dispatch_id if not isSequence(bindto): raise ValueError, "bindto is required argument and must be a sequence" self.session_id = '%s/dsp/dispatch:%d' % (session_id, dispatch_id) dispatch_id = dispatch_id + 1 self.dispatches = [] prio = kw.pop('prio', ZD_PRI_LISTEN) self.transparent = kw.setdefault('transparent', FALSE) self.rule_port = self._parsePortString(kw.pop('rule_port', "")) if kw == None: kw = {} for b in bindto: self.dispatches.append(Dispatch(self.session_id, b, prio, self.accepted, kw)) Globals.dispatches.append(self) @staticmethod def _parsePortString(s): """ """ if not s: return [] if type(s) == int: return [(s, s)] ranges = [] for p in s.split(","): c = p.count(":") if c == 0: ranges.append((int(p), int(p))) elif c == 1: (start, end) = p.split(":")[:2] ranges.append((int(start), int(end))) else: raise ValueError, "Invalid port range: '%s'" % (p) return ranges def accepted(self, stream, client_address, client_local, client_listen): """ Callback to inform the python layer about incoming connections. This callback is called by the core when a connection is accepted. Its primary function is to check access control (whether the client is permitted to connect to this port), and to spawn a new session to handle the connection. Exceptions raised due to policy violations are handled here. Returns TRUE if the connection is accepted stream the stream of the connection to the client client_address the address of the client client_local client local address (contains the original destination if transparent) client_listen the address where the listener was bound to """ if stream == None: return None session = None try: session = MasterSession() session.setProtocol(client_listen.protocol) stream.name = session.session_id session.client_stream = stream session.client_local = client_local session.client_listen = client_listen session.setClientAddress(client_address) service = self.getService(session) if not service: raise DACException, "No applicable service found" session.setService(service) service.router.routeConnection(session) if session.isClientPermitted() == ZV_ACCEPT: ## LOG ## # This message indicates that a new connection is accepted. ## log(session.session_id, CORE_DEBUG, 8, "Connection accepted; client_address='%s'", (client_address,)) sys.exc_clear() stream.keepalive = service.keepalive & Z_KEEPALIVE_CLIENT; if session.service.startInstance(session): return TRUE else: raise DACException, "This service was not permitted outbound" except ZoneException, s: ## LOG ## # This message indicates that no appropriate zone was found for the client address. # @see: Zone ## log(session.session_id, CORE_POLICY, 1, "Zone not found; info='%s'", (s,)) except DACException, s: ## LOG ## # This message indicates that an DAC policy violation occurred. # It is likely that the new connection was not permitted as an outbound_service in the given zone. # @see: Zone ## log(session.session_id, CORE_POLICY, 1, "DAC policy violation; info='%s'", (s,)) except MACException, s: ## LOG ## # This message indicates that a MAC policy violation occurred. ## log(session.session_id, CORE_POLICY, 1, "MAC policy violation; info='%s'", (s,)) except AAException, s: ## LOG ## # This message indicates that an authentication failure occurred. # @see: Auth ## log(session.session_id, CORE_POLICY, 1, "Authentication failure; info='%s'", (s,)) except LimitException, s: ## LOG ## # This message indicates that the maximum number of concurrent instance number is reached. # Try increase the Service "max_instances" attribute. # @see: Service.Service ## log(session.session_id, CORE_POLICY, 1, "Connection over permitted limits; info='%s'", (s,)) except LicenseException, s: ## LOG ## # This message indicates that the licensed number of IP address limit is reached, and no new IP address is allowed or an unlicensed component is used. # Check your license's "Licensed-Hosts" and "Licensed-Options" options. ## log(session.session_id, CORE_POLICY, 1, "Attempt to use an unlicensed component, or number of licensed hosts exceeded; info='%s'", (s,)) except RuntimeError, s: log(session.session_id, CORE_POLICY, 1, "Unexpected runtime error occured; info='%s'", (s,)) except: print_exc() if session != None: session.destroy() return None def getService(self, session): """ Get the service associated with the session Returns the service to start. session session reference """ return None def destroy(self): """ Stops the listener on the given port Calls the destroy method of the low-level object """ for d in self.dispatches: d.destroy() class RuleDispatcher(BaseDispatch): def __init__(self, **kw): """ """ self.bindto = [] self.bindto.append(DBSockAddr(SockAddrInet("127.0.0.1", 0), ZD_PROTO_TCP)) self.bindto.append(DBSockAddr(SockAddrInet("127.0.0.1", 0), ZD_PROTO_UDP)) self.bindto.append(DBSockAddr(SockAddrInet6("::1", 0), ZD_PROTO_TCP)) self.bindto.append(DBSockAddr(SockAddrInet6("::1", 0), ZD_PROTO_UDP)) kw['transparent'] = TRUE super(RuleDispatcher, self).__init__(Globals.instance_name, self.bindto, **kw) def getService(self, session): # query the KZorp result for the client fd of this session fd = session.client_stream.fd result = getKZorpResult(session.client_address.family, fd) if (result): (client_zone_name, server_zone_name, dispatcher_name, service_name) = result return Globals.services.get(service_name) else: ## LOG ## # This message indicates that the KZorp result # lookup has failed for this session. ## log(None, CORE_POLICY, 0, "Unable to determine service, KZorp service lookup failed; bindto='%s'", (self.bindto[0], )) return None def buildKZorpMessage(self): """ """ messages = [] messages.append((kznf.KZNL_MSG_ADD_DISPATCHER, kznf.create_add_dispatcher_n_dimension(self.session_id, 0, 0, Globals.rules.length))) for rule in Globals.rules: messages.extend(rule.buildKZorpMessage(self.session_id)) return messages def buildKZorpBindMessage(self): """ """ messages = [] for bind in self.bindto: messages.append((kznf.KZNL_MSG_ADD_BIND, kznf.NfnetlinkMessageAddBind(bind.sa.family, Globals.instance_name, bind.sa.pack(), bind.sa.port, convertDBProtoToIPProto(bind.protocol)))) return messages @classmethod def createOneInstance(cls): if not hasattr(cls, "_singletonRuleDispatcher"): cls._singletonRuleDispatcher = RuleDispatcher() class Dispatcher(BaseDispatch): """ Class encapsulating the Dispatcher which starts a service by the client and server zone. This class is the starting point of Zorp services. It listens on the given port, and when a connection is accepted it starts a session and the given service. Dispatcher example The following example defines a transparent dispatcher that starts the service called demo_http_service for connections received on the 192.168.2.1 IP address. Dispatcher(bindto=SockAddrInet('192.168.2.1', 50080), service="demo_http_service", transparent=TRUE, backlog=255, threaded=FALSE) service Name of the service to start. bindto An existing socket address containing the IP address and port number where the Dispatcher accepts connections. protocol the protocol we were bound to backlog Applies only to TCP connections. This parameter sets the queue size (maximum number) of TCP connections that are established by the kernel, but not yet accepted by Zorp. This queue stores the connections that successfully performed the three-way TCP handshake with the Zorp host, until the dispatcher sends the Accept package. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the dispatcher will start from the dispatcher's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. """ def __init__(self, bindto=None, service=None, **kw): """ Constructor to initialize a Dispatcher instance. This constructor creates a new Dispatcher instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Dispatcher accepts connections. service Name of the service to start. transparent Set this parameter to TRUE if the dispatcher starts a transparent service. """ try: if service != None: self.service = Globals.services[service] else: self.service = None except KeyError: raise ServiceException, "Service %s not found" % (service,) bindto = makeSequence(bindto) # make sure that bind addresses are consistent wrt. the protocol self.protocol = ZD_PROTO_AUTO for b in bindto: if b.protocol == ZD_PROTO_AUTO: raise ValueError, "No preferred protocol is specified" if b.protocol != self.protocol: if self.protocol == ZD_PROTO_AUTO: self.protocol = b.protocol else: raise ValueError, "Inconsistent protocol specified in dispatch addresses" self.bindto = bindto super(Dispatcher, self).__init__(Globals.instance_name, bindto, **kw) def getService(self, session): """ Returns the service associated with the listener Returns the service to start. session session reference """ return self.service def buildKZorpMessage(self): """ """ messages = [] # FIXME: handle multiple dispatch bind addresses flags = 0 if self.transparent: flags = flags | kznf.KZF_DPT_TRANSPARENT if self.__dict__.has_key("follow_parent") and self.follow_parent: flags = flags | kznf.KZF_DPT_FOLLOW_PARENT bindto = self.bindto[0] if isinstance(bindto, DBSockAddrType): # SA bind if self.rule_port == []: rule_port = [(bindto.sa.port, bindto.sa.port)] else: rule_port = self.rule_port if (bindto.sa.get_family() == socket.AF_INET): messages.append((kznf.KZNL_MSG_ADD_DISPATCHER, kznf.create_add_dispatcher_sabind_msg(bindto.format(), flags, convertDBProtoToIPProto(bindto.protocol), bindto.sa.port, socket.ntohl(bindto.sa.ip), rule_port))) else: log(None, CORE_ERROR, 2, "KZorp does not support SockAddr bind dispatchers bound to a non-IPv4 socket") elif isinstance(bindto, DBIfaceType): # interface bind if self.rule_port == []: rule_port = [(bindto.port, bindto.port)] else: rule_port = self.rule_port messages.append((kznf.KZNL_MSG_ADD_DISPATCHER, kznf.create_add_dispatcher_ifacebind_msg(bindto.format(), flags, convertDBProtoToIPProto(bindto.protocol), bindto.port, bindto.iface, rule_port, socket.ntohl(bindto.ip)))) elif isinstance(bindto, DBIfaceGroupType): # interface group bind if self.rule_port == []: rule_port = [(bindto.port, bindto.port)] else: rule_port = self.rule_port messages.append((kznf.KZNL_MSG_ADD_DISPATCHER, kznf.create_add_dispatcher_ifgroupbind_msg(bindto.format(), flags, convertDBProtoToIPProto(bindto.protocol), bindto.port, bindto.group, 0xffffffff, rule_port))) if self.service: messages.append((kznf.KZNL_MSG_ADD_DISPATCHER_CSS, kznf.create_add_dispatcher_css_msg(bindto.format(), self.service.name))) return messages def buildKZorpBindMessage(self): """ """ return [] class ZoneDispatcher(Dispatcher): """ Class encapsulating the Dispatcher which starts a service by the client zone. This class is similar to a simple Dispatcher, but instead of starting a fixed service, it chooses one based on the client zone. It takes a mapping of services indexed by a zone name, with an exception of the '*' service, which matches anything. services services mapping indexed by zone name """ def __init__(self, bindto=None, services=None, **kw): """ Constructor to initialize a ZoneDispatcher instance. This constructor initializes a ZoneDispatcher instance and sets its initial attributes based on arguments. bindto bind to this address services a mapping between zone names and services follow_parent whether to follow the administrative hieararchy when finding the correct service """ self.follow_parent = kw.pop('follow_parent', FALSE) super(ZoneDispatcher, self).__init__(bindto, None, **kw) self.services = services self.cache = ShiftCache('sdispatch(%s)' % str(bindto), config.options.zone_dispatcher_shift_threshold) def getService(self, session): """ Virtual function which returns the service to be ran This function is called by our base class to find out the service to be used for the current session. It uses the client zone name to decide which service to use. session session we are starting """ cache_ndx = session.client_zone.getName() cached = self.cache.lookup(cache_ndx) if cached == 0: ## LOG ## # This message indicates that no applicable service was found for this client zone in the services cache. # It is likely that there is no applicable service configured in this ZoneListener/Receiver at all. # Check your ZoneListener/Receiver service configuration. # @see: Listener.ZoneListener # @see: Receiver.ZoneReceiver ## log(None, CORE_POLICY, 2, "No applicable service found for this client zone (cached); bindto='%s', client_zone='%s'", (self.bindto, session.client_zone)) elif cached: return cached src_hierarchy = {} if self.follow_parent: z = session.client_zone level = 0 while z: src_hierarchy[z.getName()] = level z = z.admin_parent level = level + 1 src_hierarchy['*'] = level max_level = level + 1 else: src_hierarchy[session.client_zone.getName()] = 0 src_hierarchy['*'] = 1 max_level = 10 best = None for spec in self.services.keys(): try: src_level = src_hierarchy[spec] except KeyError: src_level = max_level if not best or \ (best_src_level > src_level): best = self.services[spec] best_src_level = src_level s = None if best_src_level < max_level: try: s = Globals.services[best] except KeyError: log(None, CORE_POLICY, 2, "No such service; service='%s'", (best)) else: ## LOG ## # This message indicates that no applicable service was found for this client zone. # Check your ZoneListener/Receiver service configuration. # @see: Listener.ZoneListener # @see: Receiver.ZoneReceiver ## log(None, CORE_POLICY, 2, "No applicable service found for this client zone; bindto='%s', client_zone='%s'", (self.bindto, session.client_zone)) self.cache.store(cache_ndx, s) return s def buildKZorpMessage(self): """ """ messages = super(ZoneDispatcher, self).buildKZorpMessage() # FIXME: check that the service exists bindto = self.bindto[0] for zone in services.keys(): messages.append((kznf.KZNL_MSG_ADD_DISPATCHER_CSS, kznf.create_add_dispatcher_css_msg(bindto.format(), self.services[zone], zone))) return messages class CSZoneDispatcher(Dispatcher): """ Class encapsulating the Dispatcher which starts a service by the client and server zone. This class is similar to a simple Dispatcher, but instead of starting a fixed service, it chooses one based on the client and the destined server zone. It takes a mapping of services indexed by a client and the server zone name, with an exception of the '*' zone, which matches anything. NOTE: the server zone might change during proxy and NAT processing, therefore the server zone used here only matches the real destination if those phases leave the server address intact. CSZoneDispatcher example The following example defines a CSZoneDispatcher that starts the service called internet_HTTP_DMZ for connections received on the 192.168.2.1 IP address, but only if the connection comes from the internet zone and the destination is in the DMZ zone. CSZoneDispatcher(bindto=SockAddrInet('192.168.2.1', 50080), services={("internet", "DMZ"):"internet_HTTP_DMZ"}, transparent=TRUE, backlog=255, threaded=FALSE, follow_parent=FALSE) services services mapping indexed by zone names """ def __init__(self, bindto=None, services=None, **kw): """ Constructor to initialize a CSZoneDispatcher instance. This constructor initializes a CSZoneDispatcher instance and sets its initial attributes based on arguments. bindto An existing socket address containing the IP address and port number where the Dispatcher accepts connections. services HASH;STRING_zone,STRING_zone;STRING_service Client zone - server zone - service name pairs using the (("client_zone","server_zone"):"service") format; specifying the service to start when the dispatcher accepts a connection from the given client zone that targets the server zone. follow_parent Set this parameter to TRUE if the dispatcher handles also the connections coming from the child zones of the selected client zones. Otherwise, the dispatcher accepts traffic only from the explicitly listed client zones. """ self.follow_parent = kw.pop('follow_parent', FALSE) super(CSZoneDispatcher, self).__init__(bindto, None, **kw) self.services = services self.cache = ShiftCache('csdispatch(%s)' % str(self.bindto), config.options.zone_dispatcher_shift_threshold) def getService(self, session): """ Virtual function which returns the service to be ran This function is called by our base class to find out the service to be used for the current session. It uses the client and the server zone name to decide which service to use. session session we are starting """ dest_zone = Zone.lookup(session.client_local) cache_ndx = (session.client_zone.getName(), dest_zone.getName()) cached = self.cache.lookup(cache_ndx) if cached == 0: ## LOG ## # This message indicates that no applicable service was found for this client zone in the services cache. # It is likely that there is no applicable service configured in this CSZoneListener/Receiver at all. # Check your CSZoneListener/Receiver service configuration. # @see: Listener.CSZoneListener # @see: Receiver.CSZoneReceiver ## log(None, CORE_POLICY, 2, "No applicable service found for this client & server zone (cached); bindto='%s', client_zone='%s', server_zone='%s'", (self.bindto, session.client_zone, dest_zone)) elif cached: return cached src_hierarchy = {} dst_hierarchy = {} if self.follow_parent: z = session.client_zone level = 0 while z: src_hierarchy[z.getName()] = level z = z.admin_parent level = level + 1 src_hierarchy['*'] = level max_level = level + 1 z = dest_zone level = 0 while z: dst_hierarchy[z.getName()] = level z = z.admin_parent level = level + 1 dst_hierarchy['*'] = level max_level = max(max_level, level + 1) else: src_hierarchy[session.client_zone.getName()] = 0 src_hierarchy['*'] = 1 dst_hierarchy[dest_zone.getName()] = 0 dst_hierarchy['*'] = 1 max_level = 10 best = None for spec in self.services.keys(): try: src_level = src_hierarchy[spec[0]] dst_level = dst_hierarchy[spec[1]] except KeyError: src_level = max_level dst_level = max_level if not best or \ (best_src_level > src_level) or \ (best_src_level == src_level and best_dst_level > dst_level): best = self.services[spec] best_src_level = src_level best_dst_level = dst_level s = None if best_src_level < max_level and best_dst_level < max_level: try: s = Globals.services[best] except KeyError: log(None, CORE_POLICY, 2, "No such service; service='%s'", (best)) else: ## LOG ## # This message indicates that no applicable service was found for this client zone. # Check your CSZoneListener/Receiver service configuration. # @see: Listener.CSZoneListener # @see: Receiver.CSZoneReceiver ## log(None, CORE_POLICY, 2, "No applicable service found for this client & server zone; bindto='%s', client_zone='%s', server_zone='%s'", (self.bindto, session.client_zone, dest_zone)) self.cache.store(cache_ndx, s) return s def buildKZorpMessage(self): """ """ messages = super(CSZoneDispatcher, self).buildKZorpMessage() # FIXME: check that the service exists bindto = self.bindto[0] for (c_zone, s_zone) in self.services.keys(): messages.append((kznf.KZNL_MSG_ADD_DISPATCHER_CSS, kznf.create_add_dispatcher_css_msg(bindto.format(), self.services[(c_zone, s_zone)], c_zone, s_zone))) return messages class NDimensionDispatcher(Dispatcher): """ Class encapsulating an N Dimension Dispatcher. NDimensionDispatcher example The following example defines an NDimensionDispatcher that starts the service called internet_HTTP_DMZ for connections received on the 192.168.2.1 IP address and on the eth0 interface, but only if the connection comes from the internet zone and the destination is in the DMZ zone. NDimensionDispatcher(bindto=DBSockAddr(SockAddrInet('0.0.0.0', 50010), ZD_PROTO_TCP), rules=( { 'iface': "eth0", 'proto': socket.IPPROTO_TCP, 'dst_port': "80", 'src_zone': 'internet', 'dst_zone': 'DMZ', 'service': 'internet_HTTP_DMZ' } ) ) services services mapping indexed by zone names """ def __init__(self, bindto=None, rules=None, **kw): """ Constructor to initialize a NDimensionDispatcher instance. This constructor initializes a NDimensionDispatcher instance and sets its initial attributes based on arguments. bindto An existing socket address containing the IP address and port number where the Dispatcher accepts connections. """ super(NDimensionDispatcher, self).__init__(bindto, None, **kw) self.rules = rules def getService(self, session): """ Returns the service associated with the listener Returns the service to start. session session reference """ # query the KZorp result for the client fd of this session fd = session.client_stream.fd result = getKZorpResult(session.client_address.family, fd) if (result): (client_zone_name, server_zone_name, dispatcher_name, service_name) = result return Globals.services.get(service_name) else: ## LOG ## # This message indicates that the KZorp result # lookup has failed for this session. ## log(None, CORE_POLICY, 0, "Unable to determine service, KZorp service lookup failed; bindto='%s'", (self.bindto[0], )) return None def buildKZorpMessage(self): """ """ flags = 0 if self.transparent: flags = flags | kznf.KZF_DPT_TRANSPARENT messages = [] dimension_names = { 'auth' : kznf.KZA_N_DIMENSION_AUTH, \ 'iface' : kznf.KZA_N_DIMENSION_IFACE, \ 'ifgroup' : kznf.KZA_N_DIMENSION_IFGROUP, \ 'proto' : kznf.KZA_N_DIMENSION_PROTO, \ 'src_port' : kznf.KZA_N_DIMENSION_SRC_PORT, \ 'dst_port' : kznf.KZA_N_DIMENSION_DST_PORT, \ 'src_subnet' : kznf.KZA_N_DIMENSION_SRC_IP, \ 'src_subnet6' : kznf.KZA_N_DIMENSION_SRC_IP6, \ 'src_zone' : kznf.KZA_N_DIMENSION_SRC_ZONE, \ 'dst_subnet' : kznf.KZA_N_DIMENSION_DST_IP, \ 'dst_subnet6' : kznf.KZA_N_DIMENSION_DST_IP6, \ 'dst_zone' : kznf.KZA_N_DIMENSION_DST_ZONE } def generateRuleId(self): """ """ while (self._rule_id_index in self.rule_id_set): self._rule_id_index += 1 self.rule_id_set.add(self._rule_id_index) return self._rule_id_index def parseSubnets(subnet_list): subnets = { socket.AF_INET: [], socket.AF_INET6: [] } subnet_list = makeSequence(subnet_list) for subnet_str in subnet_list: subnet = Subnet.create(subnet_str) subnets[subnet.get_family()].append((subnet.addr_packed(), subnet.netmask_packed())) return (tuple(subnets[socket.AF_INET]), tuple(subnets[socket.AF_INET6])) def parsePorts(range_list): ranges = [] range_list = makeSequence(range_list) for range_str in range_list: ranges.extend(self._parsePortString(range_str)) return tuple(ranges) self.rule_id_set = set() self._rule_id_index = 1 self.rules = makeSequence(self.rules) bindto = self.bindto[0] messages.append((kznf.KZNL_MSG_ADD_DISPATCHER, kznf.create_add_dispatcher_n_dimension(bindto.format(), flags, bindto.sa.port, len(self.rules)))) # check for duplicate rule IDs for rule in self.rules: if 'rule_id' in rule: rule_id = rule['rule_id'] if rule_id not in self.rule_id_set: self.rule_id_set.add(rule_id) else: raise ValueError, "Duplicated rule_id found; rule_id=%d" % (rule_id) # generate rule IDs for rules not containing one for rule in self.rules: if not 'rule_id' in rule: rule['rule_id'] = generateRuleId(self) # sort self.rules so that rule IDs are in increasing order if self.rules: rule_list = list(self.rules) rule_list.sort(lambda a, b: cmp(a['rule_id'], b['rule_id'])) self.rules = rule_list for rule in self.rules: entry_nums = {} if 'service' not in rule: raise ValueError, "Service key not found in rule description" # remove pseudo-dimensions: these are not real # dimensions, but rather properties of the rule service = rule['service'] del rule['service'] rule_id = rule['rule_id'] del rule['rule_id'] # parse and separate IPv4 and IPv6 subnets (rule['src_subnet'], rule['src_subnet6']) = parseSubnets(rule.get('src_subnet', [])) (rule['dst_subnet'], rule['dst_subnet6']) = parseSubnets(rule.get('dst_subnet', [])) # parse port range strings rule['src_port'] = parsePorts(rule.get('src_port', [])) rule['dst_port'] = parsePorts(rule.get('dst_port', [])) # determine length of each dimension for k, v in rule.items(): if k not in dimension_names: raise ValueError, "Dimension name='%s' not found" % (k) dimension = dimension_names[k] if k not in entry_nums: entry_nums[dimension] = 0 if isSequence(v): entry_nums[dimension] += len(v) else: entry_nums[dimension] += 1 messages.append((kznf.KZNL_MSG_ADD_RULE, kznf.create_add_n_dimension_rule_msg(bindto.format(), rule_id, service, entry_nums))) if (entry_nums.values() == []): _max = 0 else: _max = max(entry_nums.values()) for i in xrange(_max): data = {} for k, v in rule.items(): nf_k = dimension_names[k] if nf_k in entry_nums and entry_nums[nf_k] > i: if isSequence(v): data[nf_k] = v[i] else: data[nf_k] = v messages.append((kznf.KZNL_MSG_ADD_RULE_ENTRY, kznf.create_add_n_dimension_rule_entry_msg(bindto.format(), rule_id, data))) return messages def purgeDispatches(): """ """ for i in Globals.dispatches: i.destroy() del Globals.dispatches Globals.deinit_callbacks.append(purgeDispatches) zorp-3.9.5/pylib/Zorp/Domain.py000066400000000000000000000473311172670260400163710ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to address domains. This module implements the AbstractDomain class and its derived classes, which encapsulate a set of physical addresses. """ from Zorp import * from SockAddr import SockAddrInet, inet_ntoa, inet_aton from string import split, atoi from socket import htonl, ntohl try: from SockAddr import inet_pton, inet_ntop except ImportError: pass class AbstractDomain: """ Abstract base class for address domains. An address domain encapsulates an address type (AF_INET, AF_INET6, etc.) and provides functions to parse and compare these addresses. This functionality is primarily used by Zone classes (see the Service module). family The address family used by the domain. """ family = AF_UNSPEC def __init__(self): """ Constructor initializing an instance of the AbstractDomain class. This constructor is empty and does nothing. """ pass def __cmp__(self, other): """ Function to compare two domains. This function is a placeholder and is to be overridden by derived classes. It should return -1, 0 or 1 for relations less-than, equal-to and greater-than, respectively. other The instance compared to this instance. """ raise NotImplementedError def contains(self, other): """ """ try: if self > other: return TRUE except ValueError: pass return FALSE def getHostAddr(self, addr): """ """ raise NotImplementedError def mapHostAddr(self, addr): """ """ raise NotImplementedError class InetDomain(AbstractDomain): """ Class representing IP address ranges, derived from Domain. A class representing Internet (IPv4) addresses, and IP segments. The address is represented in the XXX.XXX.XXX.XXX/M format, where XXX.XXX.XXX.XXX is the network address, and M specifies the number of ones (1) in the netmask. Two InetDomain objects can be compared to each other, and can have the followin relations: Class_A can contain Class_B; Class_A can be equal to Class_B; Class_B can contain Class_A. "equal to" and "contained by". This comparison is used to organize Zones hierarchically. The comparison can raise ValueError for incomparable IP addresses. mask_bits Number of bits in the netmask. mask Netmask in network byte order. ip Network address in network byte order. """ def __init__(self, addr=None): """ Constructor to initialize an InetDomain instance This constructor parses the argument addr and fills instance attributes accordingly. addr The string representation of an address, or network address in network byte order. """ #FIXME: input validation! self.family = AF_INET if not addr: addr = '0.0.0.0/0' parts = split(addr,'/') try: self.mask_bits = atoi(parts[1]) self.mask = htonl(((1 << self.mask_bits) - 1) << (32 - self.mask_bits)) except IndexError: self.mask_bits = 32 self.mask = htonl(0xffffffff) self.ip = inet_aton(parts[0]) & self.mask def __str__(self): """ Function returning the string representation of this instance. This function returns the string representation of this instance in the form address/mask. """ return "%s/%u" % (inet_ntoa(self.ip), self.mask_bits) def __cmp__(self, other): """ Function to compare this instance to another. This function compares this instance to another InetDomain instance or to a SockAddrInet instance using set inclusion on addresses. An address is less than another, if it's fully contained by other. Returns an integer representing the relation between self and other (-1, 0, or 1) other the other InetDomain object to compare to """ if isinstance(other, InetDomain): if ((self.ip == other.ip) & (self.netmask() == other.netmask())): return 0 if ((self.mask_bits >= other.mask_bits) & ((self.ip & other.netmask()) == other.ip)): return -1 if ((other.mask_bits >= self.mask_bits) & ((other.ip & self.netmask()) == self.ip)): return 1 else: try: if ((other.ip & self.netmask()) == self.ip): return 1 except AttributeError: pass raise ValueError, '%s and %s are incomparable' % (self, other) def getHostAddr(self, addr): """ """ return addr.ip & ~self.netmask() def mapHostAddr(self, addr): """ """ return self.netaddr() + (addr & ~self.netmask()) def netaddr(self): """ Function calculating the network address of this address range. This function returns the network address of the address range represented by this instance. Returns ip address in network byte order """ return self.ip def broadcast(self): """ Function calculating the broadcast address of this address range This function returns the broadcast address of this domain calculated based on attributes. Returns the broadcast address in network byte order """ if self.mask_bits == 0: return self.ip return htonl(ntohl(self.ip) | (0x7fffffff >> (self.mask_bits - 1))) def netmask(self): """ Function to calculate the netmask of this address range This function calculates and returns the netmask of this address range as an integer in network byte order. Returns the network mask as ip in network byte order """ return self.mask class Inet6Domain(AbstractDomain): """ Class representing IPv6 address ranges, derived from Domain. A class representing Internet (IPv6) addresses, and IP segments. The address is represented in the XXX:XXX:XXX:XXX:XXX:XXX:XXX:XXX/M format, where XXX:XXX:XXX:XXX:XXX:XXX:XXX:XXX is the network address, and M specifies the number of ones (1) in the netmask. Two Inet6Domain objects can be compared to each other, and can have the followin relations: Class_A can contain Class_B; Class_A can be equal to Class_B; Class_B can contain Class_A. "equal to" and "contained by". This comparison is used to organize Zones hierarchically. The comparison can raise ValueError for incomparable IP addresses. mask_bits Number of bits in the netmask. mask Netmask represented by a tuple of eight 16-bit numbers. ip Network address represented by a tuple of eight 16-bit numbers. """ def __init__(self, addr=None): """ Constructor to initialize an Inet6Domain instance. This constructor parses the argument addr and fills instance attributes accordingly. addr String representation of an address or address range. """ def calculate_mask(number): mask=() while number > 0: n = min(number, 16) v = htonl(((1 << n) - 1) << (16 - n)) mask = mask + (v,) number = number - n mask = mask + (0, ) * (8 - len(mask)) return mask if not addr: addr = '0::0/0' #FIXME: input validation! self.family = AF_INET6 parts = split(addr,'/') try: self.mask_bits = atoi(parts[1]) self.mask = calculate_mask(self.mask_bits) except IndexError: self.mask_bits = 128 self.mask = (65535,) * 8 self.ip = map(lambda x,y: x&y, inet_pton(AF_INET6, parts[0]), self.mask) def __str__(self): """ Function returning the string representation of this instance This function returns the string representation of this instance in the form address/mask. Returns a string representing this object """ return "%s/%u" % (inet_ntop(AF_INET6, self.ip), self.mask_bits) def __cmp__(self, other): """ Function to compare this instance to another This function compares this instance to another InetDomain instance or to a SockAddrInet instance using set inclusion on addresses. An address is less than another, if it's fully contained by other. Returns an integer representing the relation between self and other (-1, 0, or 1) other the other InetDomain object to compare to """ if isinstance(other, Inet6Domain): if ((self.ip == other.ip) & (self.netmask() == other.netmask())): return 0 if ((self.mask_bits >= other.mask_bits) & (map(lambda x,y: x&y, self.ip, other.netmask()) == other.ip)): return -1 if ((other.mask_bits >= self.mask_bits) & (map(lambda x,y: x&y, other.ip, self.netmask()) == self.ip)): return 1 else: try: if (map(lambda x,y: x&y, other.ip, self.netmask()) == self.ip): return 1 except AttributeError: pass raise ValueError, '%s and %s are incomparable' % (self, other) def netaddr(self): """ Function calculating the network address of this address range This function returns the network address of the address range represented by this instance. Returns ip address in network byte order """ return self.ip def netmask(self): """ Function to calculate the netmask of this address range This function calculates and returns the netmask of this address range as an integer in network byte order. Returns the network mask as ip in network byte order """ return self.mask zorp-3.9.5/pylib/Zorp/Encryption.py000066400000000000000000001671751172670260400173250ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Encryption module defines encryption related policies. FIXME: fill here. Starting with Zorp 3.3FR1, the Proxy module provides a common SSL/TLS framework for the Zorp proxies as well. This SSL framework replaces and extends the functionality of the Pssl proxy, providing support for STARTTLS as well. The Pssl proxy has become obsolete, but still provides a compatibility layer for older configuration files, but you are recommended to update your configuration to use the new SSL framework as soon as possible. The SSL framework is described in . STARTTLS support is currently available only for the Ftp proxy to support FTPS sessions and for the SMTP proxy. Certificate verification settings SSL_VERIFY_NONE Automatic certificate verification is disabled. SSL_VERIFY_OPTIONAL_UNTRUSTED Certificate is optional, if present, both trusted and untrusted certificates are accepted. SSL_VERIFY_OPTIONAL_TRUSTED Certificate is optional, but if a certificate is present, only certificates signed by a trusted CA are accepted. SSL_VERIFY_REQUIRED_UNTRUSTED Valid certificate is required, both trusted and untrusted certificates are accepted. SSL_VERIFY_REQUIRED_TRUSTED Certificate is required, only valid certificates signed by a trusted CA are accepted. Constants for SSL/TLS protocol selection SSL_METHOD_SSLV23 Permit the use of SSLv2 and v3. SSL_METHOD_SSLV2 Permit the use of SSLv2 exclusively. SSL_METHOD_SSLV3 Permit the use of SSLv3 exclusively. SSL_METHOD_TLSV1 Permit the use of TLSv1 exclusively. SSL_METHOD_ALL Permit the use of all the supported (SSLv2, SSLv3, and TLSv1) protocols. Constants for cipher selection SSL_CIPHERS_ALL Permit the use of all supported ciphers, including the 40 and 56 bit exportable ciphers. SSL_CIPHERS_HIGH Permit only the use of ciphers which use at least 128 bit long keys. SSL_CIPHERS_MEDIUM Permit only the use of ciphers which use 128 bit long keys. SSL_CIPHERS_LOW Permit only the use of ciphers which use keys shorter then 128 bits. Handshake order. SSL_HSO_CLIENT_SERVER Perform the SSL-handshake with the client first. SSL_HSO_SERVER_CLIENT Perform the SSL-handshake with the server first. Client connection security type. SSL_NONE Disable encryption between Zorp and the peer. SSL_FORCE_SSL Require encrypted communication between Zorp and the peer. SSL_ACCEPT_STARTTLS Permit STARTTLS sessions. Currently supported only in the Ftp proxy. Server connection security type. SSL_NONE Disable encryption between Zorp and the peer. SSL_FORCE_SSL Require encrypted communication between Zorp and the peer. SSL_FORWARD_STARTTLS Forward STARTTLS requests to the server. Currently supported only in the Ftp proxy. Verbosity level of the log messages SSL_ERROR Log only errors of the SSL framework. SSL_DEBUG Enable verbose logging of the SSL framework. Handshake policy decisions SSL_HS_ACCEPT 0 Accept the connection. SSL_HS_REJECT 1 Reject the connection. SSL_HS_POLICY 6 Use a policy to decide about the connection. SSL_HS_VERIFIED 10 """ import Globals from Keybridge import X509KeyBridge from Zorp import log, CORE_POLICY class EncryptionPolicy(object): """ Class encapsulating named encryption settings. This class encapsulates a name and an associated Encryption settings instance. Encryption policies provide a way to re-use encryption settings without having to define encryption settings for each service individually. """ def __init__(self, name, encryption): """ Constructor to create an encryption policy. This constructor initializes an encryption policy. name Name identifying the encryption policy. encryption """ self.name = name self.encryption = encryption if Globals.encryption_policies.has_key(name): raise ValueError, "Duplicate encryption policy name name: %s" % name Globals.encryption_policies[name] = self def apply(self, proxy): self.encryption.apply(proxy) def getEncryptionPolicy(name): """ """ if name: if Globals.encryption_policies.has_key(name): return Globals.encryption_policies[name] else: log(None, CORE_POLICY, 3, "No such encryption policy; name='%s'", name) return None class Encryption(object): """ Class encapsulating the abstract encryption settings. """ def __init__(self): """ """ super(Encryption, self).__setattr__('settings', {}) def __setattr__(self, name, value): """ """ self.settings[name] = value def __getattr__(self, name): """ """ if self.settings.has_key(name): return self.settings[name] else: raise AttributeError, "No such attribute: %s" % name def config(self): """ """ pass def __post_config__(self): """ """ pass def apply(self, proxy): """ """ self.config() self.__post_config__() class TLSEncryption(Encryption): """ Class encapsulating TLS encryption settings. FIXME handshake_timeout 30000 SSL handshake timeout in milliseconds. permit_invalid_certificates FALSE Accept any kind of verification failure when UNTRUSTED verify_type is set. E.g.: accept expired, self-signed, etc. certificates. permit_missing_crl TRUE This option has effect only if the CRL directories are set, that is, the attributes relevant to the connection are set: client_crl_directory, server_crl_directory, client_verify_crl_directory, server_verify_crl_directory. If Zorp does not find a CRL in these directories that matches the CAs in the certificate chain and permit_missing_crl is set to FALSE, Zorp rejects the certificate. Otherwise, the certificate is accepted even if no matching CRL is found. handshake_seq SSL_HSO_CLIENT_SERVER Handshake order. SSL_HSO_CLIENT_SERVER performs the client side handshake first, SSL_HSO_SERVER_CLIENT the server side. client_connection_security SSL_NONE Enable SSL on the client side of the proxy. This requires setting up a client private key and a certificate. client_handshake HASH:empty:RW:R empty Specifies policy callbacks for various SSL handshake phases. client_verify_type SSL_VERIFY_REQUIRED_TRUSTED Verification setting of the peer certificate on the client side. client_verify_depth 4 The longest accepted CA verification chain. client_local_privatekey The private key of the firewall on the client side. Specified as a string in PEM format. client_local_privatekey_passphrase n/a Passphrase used to access client_local_privatekey. client_local_certificate X509:empty:RW:RW empty The certificate associated to client_local_privatekey to be used on the client side. client_peer_certificate X509:empty:R:R empty The certificate returned by the peer on the client side. client_local_ca_list HASH;INTEGER;X509:empty:RW:RW empty A hash of trusted certificates. The items in this hash are used to verify client certificates. client_local_crl_list HASH;INTEGER;X509_CRL:empty:RW:RW empty A hash of Certificate Revocation Lists, associated to CA certificates in client_local_ca_list. client_ssl_method SSL_METHOD_ALL Specifies the allowed SSL/TLS protocols on the client side. client_disable_proto_sslv2 TRUE Specifies that SSLv2 should be disabled even if the method selection would otherwise support SSLv2. client_disable_proto_sslv3 FALSE Specifies that SSLv3 should be disabled even if the method selection would otherwise support SSLv3. client_disable_proto_tlsv1 FALSE Specifies that TLSv1 should be disabled even if the method selection would otherwise support TLSv1. client_ssl_cipher SSL_CIPHERS_ALL Specifies the allowed ciphers on the client side. server_connection_security SSL_NONE Enable SSL on the server side of the proxy. This requires setting up a private key and a certificate on Zorp. server_handshake HASH:empty:RW:R empty Specifies policy callbacks for various SSL handshake phases. server_verify_type SSL_VERIFY_REQUIRED_TRUSTED Verification settings of the peer certificate on the server side. server_verify_depth 4 The longest accepted CA verification chain. server_local_privatekey The private key of the firewall on the server side, specified as a string in PEM format. Server side key and certificate are optional. server_local_privatekey_passphrase n/a Passphrase used to access server_local_privatekey. server_local_certificate X509:empty:RW:RW empty The certificate to be used on the server side, associated with server_local_privatekey. server_peer_certificate X509:empty:R:R empty The certificate returned by the peer on the server side. server_local_ca_list HASH;INTEGER;X509:empty:RW:RW empty Hash of trusted certificates. The items in this hash are used to verify server certificates. server_peer_ca_list HASH;INTEGER;X509:empty:RW:RW empty Hash of names of trusted CAs as returned by the server to aid the selection of a local certificate. server_local_crl_list HASH;INTEGER;X509_CRL:empty:RW:RW empty Hash of Certificate Revocation Lists, associated to CA certificates in server_local_ca_list. server_ssl_method SSL_METHOD_ALL Specifies the SSL/TLS protocols allowed on the server side. server_disable_proto_sslv2 TRUE Specifies that SSLv2 should be disabled even if the method selection would otherwise support SSLv2. server_disable_proto_sslv3 FALSE Specifies that SSLv3 should be disabled even if the method selection would otherwise support SSLv3. server_disable_proto_tlsv1 FALSE Specifies that TLSv1 should be disabled even if the method selection would otherwise support TLSv1. server_ssl_cipher SSL_CIPHERS_ALL Specifies the ciphers allowed on the server side. server_check_subject TRUE Specifies whether the Subject of the server side certificate is checked against application layer information (e.g.: whether it matches the hostname in the URL). See also . client_cert_file File containing the client-side certificate. client_key_file File containing the client-side private key. client_keypair_files A tuple of two file names containing the certificate and key files. Using client_keypair_files is an alternative to using the client_cert_file and client_key_file attributes. client_keypair_generate FALSE Enables keybridging towards the clients. (Specifies whether to generate new certificates.) client_ca_directory "" Directory where the trusted CA certificates are stored. Note that every certificate in this directory is loaded when the proxy is starting up. If client_verify_type is set to verify client certificates, Zorp sends the subject names of CA certificates stored in this directory to the client to request a certificate from these CAs. client_verify_ca_directory "" Directory where the trusted CA certificates are stored. CA certificates are loaded on-demand from this directory when verifying the client certificate. Use this option instead of client_ca_directory if possible. Note that when using the client_verify_ca_directory option, Zorp does not send the list of accepted CAs to the client if the certificate of the client is verified. client_crl_directory "" Directory where the CRLs associated with trusted CAs are stored. Note that every CRL in this directory is loaded when the proxy is starting up and this might require a huge amount of memory. client_verify_crl_directory "" Directory where the CRLs associated with trusted CAs are stored. CRLs are loaded on-demand from this directory when verifying the client certificate. Use this option instead of client_crl_directory. client_cagroup_directories "" A tuple of the trusted CA certificate directory and the corresponding CRL directory. client_verify_cagroup_directories "" A tuple of the trusted CA certificate directory and the corresponding CRL directory. This option sets both client_verify_ca_directory and client_verify_crl_directory. client_trusted_certs_directory "" A directory where trusted IP - certificate assignments are stored. When a specific IP address introduces itself with the certificate stored in this directory, it is accepted regardless of its expiration or issuer CA. Each file in the directory should contain a certificate in PEM format and have the name of the IP address. server_cert_file File containing the server-side certificate. server_key_file File containing the server-side private key. server_keypair_files A tuple of two file names containing the certificate and key files. Using server_keypair_files is an alternative to using the server_cert_file and server_key_file attributes. server_keypair_generate FALSE Enables keybridging towards the server. (Specifies whether to generate new certificates.) server_ca_directory "" Directory where the trusted CA certificates are stored. Please note that all certificates in the directory are loaded when the proxy is starting up. server_verify_ca_directory "" Directory where the trusted CA certificates are stored. CA certificates are loaded on-demand from this directory when verifying the server certificate. Use this option instead of server_ca_directory. server_crl_directory "" Directory where the CRLs associated with the trusted CAs are stored. Please note that all CRLs in the directory are loaded when the proxy is starting up and this might require a huge amount of memory. server_verify_crl_directory "" Directory where the CRLs associated with trusted CAs are stored. CRLs are loaded on-demand from this directory when verifying the server certificate. Use this option instead of server_crl_directory. server_cagroup_directories "" A tuple of the trusted CA certificate directory and the corresponding CRL directory. This option sets both server_ca_directory and server_crl_directory. server_verify_cagroup_directories "" A tuple of the trusted CA certificate directory and the corresponding CRL directory. This option sets both server_verify_ca_directory and server_verify_crl_directory. server_trusted_certs_directory "" A directory where trusted IP:port - certificate assignments are stored. When a specific IP address introduces itself with the certificate stored in this directory, it is accepted regardless of its expiration or issuer CA. Each file in the directory should contain a certificate in PEM format and should be named as 'IP:PORT'. key_generator An instance of a X509KeyManager or derived class to generate keys automatically based on the keys on one of the other peers. Use X509KeyBridge to generate certificates automatically with a firewall hosted local CA. """ def __init__(self): """ """ super(TLSEncryption, self).__init__() def config(self): """ """ super(TLSEncryption, self).config() def apply(self, proxy): """ """ super(TLSEncryption, self).apply(proxy) for (name, value) in self.settings.items(): setattr(proxy.ssl, name, value) class TLSKeyBridgeEncryption(TLSEncryption): """ Class encapsulating the TLS keybridge encryption settings. keybridge_key_file The private key to be used for the newly generated certificates keybridge_key_passphrase Passphrase required to access the private key keybridge_cache_directory /var/lib/zorp/keybridge-cache The directory where all automatically generated certificates are cached keybridge_trusted_ca_files FIXME_keybridge_trusted_ca_files keybridge_trusted_ca_cert_file CA certificate used for keybridging trusted certificates keybridge_trusted_ca_key_file Key file for CA certificate used for keybridging trusted certificates The key file must have no passphrase. keybridge_untrusted_ca_files FIXME_keybridge_untrusted_ca_files keybridge_untrusted_ca_cert_file CA certificate used for keybridging untrusted certificates. keybridge_untrusted_ca_key_file Key file for CA certificate used for keybridging untrusted certificates. The key file must have no passphrase. """ def __init__(self): """ """ super(TLSKeyBridgeEncryption, self).__init__() def config(self): """ """ super(TLSKeyBridgeEncryption, self).config() self.keybridge_key_file = None self.keybridge_key_passphrase = None self.keybridge_cache_directory = None self.keybridge_trusted_ca_files = None self.keybridge_trusted_ca_cert_file = None self.keybridge_trusted_ca_key_file = None self.keybridge_untrusted_ca_files = None self.keybridge_untrusted_ca_cert_file = None self.keybridge_untrusted_ca_key_file = None def __post_config__(self): """ """ super(TLSKeyBridgeEncryption, self).__post_config__() if not self.keybridge_trusted_ca_files: self.keybridge_trusted_ca_files = (self.keybridge_trusted_ca_cert_file, self.keybridge_trusted_ca_key_file) if not self.keybridge_untrusted_ca_files: self.keybridge_untrusted_ca_files = (self.keybridge_untrusted_ca_cert_file, self.keybridge_untrusted_ca_key_file) self.key_generator = X509KeyBridge(self.keybridge_key_file, self.keybridge_cache_directory, self.keybridge_trusted_ca_files, self.keybridge_untrusted_ca_files, self.keybridge_key_passphrase) # Local Variables: # mode: python # indent-tabs-mode: nil # python-indent: 8 # End: zorp-3.9.5/pylib/Zorp/Exceptions.py000066400000000000000000000075561172670260400173100ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining Zorp exception types. """ class ZorpException(Exception): def __init__(self, detail): """ """ super(ZorpException, self).__init__() self.what = '' self.detail = detail def __str__(self): return '%s: %s' % (self.what, self.detail) class ZoneException(ZorpException): def __init__(self, detail): """ """ super(ZoneException, self).__init__(detail) self.what = 'Zone not found' class ServiceException(ZorpException): def __init__(self, detail): """ """ super(ServiceException, self).__init__(detail) self.what = 'Service' class DACException(ZorpException): def __init__(self, detail): """ """ super(DACException, self).__init__(detail) self.what = 'DAC policy violation' class MACException(ZorpException): def __init__(self, detail): """ """ super(MACException, self).__init__(detail) self.what = 'MAC policy violation' class AAException(ZorpException): def __init__(self, detail): """ """ super(AAException, self).__init__(detail) self.what = 'Authentication or authorization failed' # for compatibility AuthException = AAException class LimitException(ZorpException): def __init__(self, detail): """ """ super(LimitException, self).__init__(detail) self.what = 'Limit error' class InternalException(ZorpException): def __init__(self, detail): """ """ super(InternalException, self).__init__(detail) self.what = 'Internal error occured' class UserException(ZorpException): def __init__(self, detail): """ """ super(UserException, self).__init__(detail) self.what = 'Incorrect, or unspecified parameter' class LicenseException(ZorpException): def __init__(self, detail): """ """ super(LicenseException, self).__init__(detail) self.what = 'Attempt to use unlicensed components' class MatcherException(ZorpException): def __init__(self, detail): """ """ super(MatcherException, self).__init__(detail) self.what = 'Matcher error' class ConfigException(ZorpException): def __init__(self, detail): """ """ super(ConfigException, self).__init__(detail) self.what = 'Configuration error' zorp-3.9.5/pylib/Zorp/Globals.py000066400000000000000000000034031172670260400165350ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the Global. Global variables used by Zorp policy layer. """ import threading services = {} authentication_providers = {} authentication_policies = {} authorization_policies = {} neyes_waiting_authorization_policies_lock = threading.Lock() neyes_waiting_authorization_policies = {} resolver_policies = {} auth_policies = {} stacking_providers = {} zones = {} nat_policies = {} dispatches = [] rules = None auth_caches = {} matchers = {} notification_policy = None instance_name = '' external_authorization_waiting_lock = threading.Lock() external_authorization_waiting = {} kzorp_responds_to_ping = False encryption_policies = {} deinit_callbacks = [] zorp-3.9.5/pylib/Zorp/KZorp.py000066400000000000000000000123571172670260400162270ustar00rootroot00000000000000############################################################################ ## ## ############################################################################ import Globals import random, time, socket, errno import kznf.kznfnetlink from Zorp import * from Zone import Zone def netlinkmsg_handler(msg): pass def openHandle(): h = kznf.nfnetlink.Handle() s = kznf.nfnetlink.Subsystem(kznf.nfnetlink.NFNL_SUBSYS_KZORP) h.register_subsystem(s) return h def exchangeMessage(h, msg, payload): m = h.create_message(kznf.nfnetlink.NFNL_SUBSYS_KZORP, msg, kznf.nfnetlink.NLM_F_REQUEST | kznf.nfnetlink.NLM_F_ACK) m.set_nfmessage(payload) result = h.talk(m, (0, 0), netlinkmsg_handler) if result != 0: raise kznf.nfnetlink.NfnetlinkException, "Error while talking to kernel; result='%d'" % (result) def exchangeMessages(h, messages): for (msg, payload) in messages: exchangeMessage(h, msg, payload) def startTransaction(h, instance_name): tries = 7 wait = 0.1 while tries > 0: try: exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_START, \ kznf.kznfnetlink.create_start_msg(instance_name)) except socket.error, e: if e[0] == errno.ECONNREFUSED: raise except: tries = tries - 1 if tries == 0: raise wait = 2 * wait time.sleep(wait * random.random()) continue break def commitTransaction(h): exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_COMMIT, \ kznf.kznfnetlink.create_commit_msg()) def downloadServices(h): # download services exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_FLUSH_SERVICE, \ kznf.kznfnetlink.create_flush_msg()) for service in Globals.services.values(): messages = service.buildKZorpMessage() exchangeMessages(h, messages) def downloadZones(h): def walkZones(messages, zone, children): messages.extend(zone.buildKZorpMessage()) for child in children.get(zone.name, []): walkZones(messages, child, children) # download zones exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_FLUSH_ZONE, \ kznf.kznfnetlink.create_flush_msg()) # build children hash children = {} for zone in Zone.zones.values(): if zone.admin_parent: children.setdefault(zone.admin_parent.name, []).append(zone) for zone in Zone.zones.values(): if not zone.admin_parent: # tree root messages = [] walkZones(messages, zone, children) exchangeMessages(h, messages) def downloadDispatchers(h): exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_FLUSH_DISPATCHER, \ kznf.kznfnetlink.create_flush_msg()) for dispatch in Globals.dispatches: try: messages = dispatch.buildKZorpMessage() exchangeMessages(h, messages) except: log(None, CORE_ERROR, 0, "Error occured during Dispatcher upload to KZorp; dispatcher='%s', error='%s'" % (dispatch.bindto[0].format(), sys.exc_value)) raise def downloadBindAddresses(h): for dispatch in Globals.dispatches: try: messages = dispatch.buildKZorpBindMessage() exchangeMessages(h, messages) except: log(None, CORE_ERROR, 0, "Error occured during bind address upload to KZorp; dispatcher='%s', error='%s'" % (dispatch.bindto[0].format(), sys.exc_value)) raise def downloadKZorpConfig(instance_name, is_master): random.seed() h = openHandle() # start transaction startTransaction(h, instance_name) try: if is_master: downloadServices(h) downloadZones(h) downloadDispatchers(h) downloadBindAddresses(h) commitTransaction(h) except: h.close() raise Globals.kzorp_netlink_handle = h def flushKZorpConfig(instance_name): random.seed() h = getattr(Globals, "kzorp_netlink_handle", None) if not h: h = openHandle() # flush dispatchers and services startTransaction(h, instance_name) try: exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_FLUSH_DISPATCHER, \ kznf.kznfnetlink.create_flush_msg()) exchangeMessage(h, kznf.kznfnetlink.KZNL_MSG_FLUSH_SERVICE, \ kznf.kznfnetlink.create_flush_msg()) commitTransaction(h) except: h.close() raise h.close() zorp-3.9.5/pylib/Zorp/Keybridge.py000066400000000000000000000504271172670260400170670ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Keybridge module implements generic X.509 key bridging. Keybridging is a method to let the client see a copy of the server's certificate (or vice versa), allowing it to inspect it and decide about its trustworthiness. Because of proxying the SSL/TLS connection, the client is not able to inspect the certificate of the server directly, therefore Zorp generates a certificate based on the server's certificate on-the-fly. This generated certificate is presented to the client. For details on configuring keybridging, see . """ from Zorp import * import os import fcntl import OpenSSL import hashlib # # Key selector is a hash containing one or more ways to # identify a key or keypair. The meaning of various keys in the hash and how they are interpreted # is as follows: # # 'zms-key' Contains the unique name of a keypair in ZMS # 'bridge-trusted-key' Contains a certificate blob for which a new key can be generated, # the key must be signed by the 'trusted' CA # 'bridge-untrusted-key' Contains a certificate blob for which a new key can be generated, # the key must be signed by the 'untrusted' CA. # class X509KeyManager(object): """ """ def __init__(self): pass def getKeypair(self, selector): pass class X509KeyBridge(X509KeyManager): """ Class to perform SSL keybridging. This class is able to generate certificates mimicking another certificate, primarily used to transfer the information of a server's certificate to the client in keybridging. For details on configuring keybridging, see . key_file "" Name of the private key to be used for the newly generated certificates. key_passphrase "" Passphrase required to access the private key stored in key_file. cache_directory "" The directory where all automatically generated certificates are cached. trusted_ca_files None A tuple of cert_file, key_file, passphrase) for the CA used for keybridging trusted certificates. untrusted_ca_files None A tuple of cert_file, key_file, passphrase) for the CA used for keybridging untrusted certificates. """ default_extension_whitelist = ('keyUsage', 'subjectAltName', 'extendedKeyUsage') def __init__(self, key_file, cache_directory=None, trusted_ca_files=None, untrusted_ca_files=None, key_passphrase = "", extension_whitelist=None): """ key_file Name of the private key to be used for the newly generated certificates. key_passphrase "" Passphrase required to access the private key stored in key_file. cache_directory "/var/lib/zorp/keybridge-cache" The directory where all automatically generated certificates are cached. trusted_ca_files A tuple of cert_file, key_file, passphrase) for the CA used for keybridging trusted certificates. untrusted_ca_files None A tuple of cert_file, key_file, passphrase) for the CA used for keybridging untrusted certificates. extension_whitelist None Zorp transfers the following certificate extensions to the client side: Key Usage, Subject Alternative Name, Extended Key Usage. Other extensions will be automatically deleted during keybridging. This is needed because some certificate extensions contain references to the Issuer CA, which references become invalid for keybridged certificates. To transfer other extensions, list them in the extension_whitelist parameter. Note that modifying this parameter replaces the default values, so to extend the list of transferred extensions, include the 'keyUsage', 'subjectAltName', 'extendedKeyUsage' list as well. For example: self.extension_whitelist = ('keyUsage', 'subjectAltName', 'extendedKeyUsage', 'customExtension') """ """Constructor to initialize an X509KeyBridge instance This constructor initializes an X509KeyBridge instance by loading the necessary keys and certificates from files. Make sure that it is initialized once, instead of in every proxy instance as that may degrade performance. This may be achieved by putting the initialization into the class body or into global context. Arguments key_file -- name of the private key to be used for all newly generated certificates key_passphrase -- passphrase to use with private key key_file cache_directory -- name of a directory where all automatically generated certificates are cached trusted_ca_files -- a tuple of (cert_file, key_file, passphrase) for a CA to be used for signing certificates untrusted_ca_files -- a tuple of (cert_file, key_file, passphrase) for a CA to be used for signing untrusted certificates """ if cache_directory: self.cache_directory = cache_directory else: self.cache_directory = "/var/lib/zorp/keybridge-cache" if not trusted_ca_files: trusted_ca_files = (None, None, None) if not extension_whitelist: extension_whitelist = self.default_extension_whitelist self.extension_whitelist = extension_whitelist self.initialized = 0 try: self.key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open(key_file, 'r').read(), key_passphrase) try: passphrase = trusted_ca_files[2] except IndexError: passphrase = "" self.trusted_ca = (OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, open(trusted_ca_files[0], 'r').read()), OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open(trusted_ca_files[1], 'r').read(), passphrase)) if untrusted_ca_files: try: passphrase = untrusted_ca_files[2] except IndexError: passphrase = "" self.untrusted_ca = (OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, open(untrusted_ca_files[0], 'r').read()), OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open(untrusted_ca_files[1], 'r').read(), passphrase)) try: self.lock_file = open('%s/.lock' % self.cache_directory, 'r+') except IOError: self.lock_file = open('%s/.lock' % self.cache_directory, 'w') self.initialized = 1 except IOError, e: log(None, CORE_ERROR, 3, "Error opening lock, key or certificate file for keybridge; file='%s', error='%s'", (e.filename, e.strerror)) def __del__(self): """ """ if hasattr(self, "lock_file"): self.lock_file.close() def getCachedKey(self, cert_file, cert_server): """ """ try: log(None, CORE_DEBUG, 5, "Loading cached certificate; file='%s'", cert_file) try: orig_cert = open(cert_file + '.orig', 'r').read() except IOError: orig_cert = '' log(None, CORE_DEBUG, 5, "Original keybridged certificate not found, regenerating; file='%s'", cert_file) if orig_cert == cert_server: cert = open(cert_file, 'r').read() log(None, CORE_DEBUG, 5, "Cached certificate ok, reusing; file='%s'", cert_file) return (cert, OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, self.key)) else: log(None, CORE_DEBUG, 5, "Cached certificate changed, regenerating; file='%s'", cert_file) try: os.unlink(cert_file) except OSError: pass try: os.unlink(cert_file + '.orig') except OSError: pass raise KeyError, 'certificate changed' except IOError, e: # not in the cache log(None, CORE_DEBUG, 5, "I/O error loading cached certificate, regenerating; file='%s', error='%s'", (cert_file, e.strerror)) raise KeyError, 'not in the cache' def storeCachedKey(self, cert_file, new_blob, orig_blob): """ """ try: log(None, CORE_DEBUG, 5, "Storing cached certificate; file='%s'", cert_file) f = open(cert_file, 'w') f.write(new_blob) f.close() f = open(cert_file + '.orig', 'w') f.write(orig_blob) f.close() except IOError, e: log(None, CORE_ERROR, 2, "Error storing generated X.509 certificate in the cache; file='%s', error='%s'", (cert_file, e.strerror)) def lock(self): """ """ fcntl.lockf(self.lock_file, fcntl.LOCK_EX) def unlock(self): """ """ fcntl.lockf(self.lock_file, fcntl.LOCK_UN) def getLastSerial(self): """ """ serial = 1 for file in os.listdir(self.cache_directory): if file[-4:] != '.crt': continue f = open("%s/%s" % (self.cache_directory, file), 'r') data = f.read() f.close() cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, data) cser = cert.get_serial_number() if cser > serial: serial = cser return serial def genCert(self, key, orig_cert, ca_cert, ca_key, serial): """ """ filetype = OpenSSL.crypto.FILETYPE_PEM new_cert = OpenSSL.crypto.load_certificate(filetype, OpenSSL.crypto.dump_certificate(filetype, orig_cert)) new_cert.set_serial_number(serial) new_cert.set_issuer(ca_cert.get_subject()) new_cert.set_pubkey(key) if ca_key.type() == OpenSSL.crypto.TYPE_DSA: hash_alg = "DSA-SHA1" # taken from openssl-0.9.8i/crypto/objects/obj_dat.h else: hash_alg = "md5" # delete extensions not on whitelist ext_index = 0 while ext_index < new_cert.get_extension_count(): ext = new_cert.get_extension(ext_index) if ext.get_short_name() not in self.extension_whitelist: new_cert.del_extension(ext_index) else: ext_index += 1 new_cert.sign(ca_key, hash_alg) return new_cert def getKeypair(self, selector): """ """ if not self.initialized: log(None, CORE_ERROR, 3, "Keybridge not completely initialized, error generating keypair;") return (None, None) try: trusted = 1 orig_blob = selector['bridge-trusted-key'] except KeyError: trusted = 0 orig_blob = selector['bridge-untrusted-key'] hash = hashlib.md5(orig_blob).hexdigest() if trusted: cert_file = '%s/trusted-%s.crt' % (self.cache_directory, hash) ca_pair = self.trusted_ca else: cert_file = '%s/untrusted-%s.crt' % (self.cache_directory, hash) ca_pair = self.untrusted_ca self.lock() try: try: return self.getCachedKey(cert_file, orig_blob) except KeyError: log(None, CORE_DEBUG, 5, "Certificate not found in the cache, regenerating;") serial_file = '%s/serial.txt' % self.cache_directory serial_pos = "" try: serial_pos = "file open" serial_file_fd = open(serial_file, 'r') serial_pos = "file read" serial_file_data = serial_file_fd.read().strip() serial_pos = "turn to integer" serial = int(serial_file_data) serial_pos = None except (ValueError, IOError): serial = self.getLastSerial() log(None, CORE_ERROR, 3, "On-line CA serial file not found, reinitializing; file='%s', serial='%d', pos='%s'", (serial_file, serial, serial_pos)) serial = serial + 1 try: open(serial_file, 'w').write(str(serial)) except IOError, e: log(None, CORE_ERROR, 2, "Cannot write serial number of on-line CA; file='%s', error='%s'", (serial_file, e.strerror)) orig_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, orig_blob) new_cert = self.genCert(self.key, orig_cert, ca_pair[0], ca_pair[1], serial) new_blob = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, new_cert) self.storeCachedKey(cert_file, new_blob, orig_blob) return (new_blob, OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, self.key)) finally: self.unlock() zorp-3.9.5/pylib/Zorp/Listener.py000066400000000000000000000517711172670260400167520ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Listener module defines the classes that accept incoming TCP connections. Listeners bind to a specific IP address and port of the Zorp firewall and wait for incoming connection requests. For each accepted connection, the Listener creates a new service instance to handle the TCP traffic arriving in the connection. The service started by the listener depends on the type of the listener: Listeners start the same service for every connection. ZoneListeners start different services based on the zone the client belongs to. CSZoneListeners start different services based on the zones the client and the destination server belong to. Only one listener can bind to an IP address/port pair.
Zone-based service selection Listeners can start only a predefined service. Use ZoneListeners or CSZoneListeners to start different services for different connections. ZoneListener assigns different services to different client zones; CSZoneListener assigns different services to different client-server zone pairs. Define the zones and the related services in the services parameter. The * wildcard matches all client or server zones. The server zone may be modified by the proxy, the router, the chainer, or the NAT policy used in the service. To select the service, CSZoneListener determines the server zone from the original destination IP address of the incoming client request. Similarly, the client zone is determined from the source IP address of the original client request. To accept connections from the child zones of the selected client zones, set the follow_parent attribute to TRUE. Otherwise, the listener accepts traffic only from the client zones explicitly listed in the services attribute of the listener.
""" from Dispatch import * class Listener(Dispatcher): """ Class encapsulating the default Listener. Listeners listen for incoming TCP connections on a port and start a session and a service for accepted connections. Listener example The following example defines a transparent listener that starts the service called demo_http_service for connections received on the 192.168.2.1 IP address. Listener(bindto=SockAddrInet('192.168.2.1', 50080), service="demo_http_service", transparent=TRUE, backlog=255, threaded=FALSE) """ def __init__(self, bindto, service, **kw): """ Constructor to initialize an instance of the Listener class. This constructor creates a new Listener instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Listener accepts connections. service Name of the service to start. transparent Set this parameter to TRUE if the listener starts a transparent service. backlog This parameter sets the queue size (maximum number) of TCP connections that are established by the kernel, but not yet accepted by Zorp. This queue stores the connections that successfully performed the three-way TCP handshake with the Zorp host, until the listener sends the Accept package. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the listener will start from the listener's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. mark_tproxy Set this parameter to TRUE to mark all connections accepted by the Listener with the -m tproxy IPtables label. """ super(Listener, self).__init__(convertSockAddrToDB(bindto, ZD_PROTO_TCP), service, **kw) class ZoneListener(ZoneDispatcher): """ Class encapsulating a Listener which selects a service based on the client zone. ZoneListeners are similar to Listeners, but select a service based on the zone of the client. See for details. """ def __init__(self, bindto, services, **kw): """ Constructor to initialize an instance of the ZoneListener class. This constructor creates a new ZoneListener instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Listener accepts connections. services Client zone - service name pairs using the ("zone":"service") format; specifying the service to start when the listener accepts a connection from the given client zone. transparent Set this parameter to TRUE if the listener starts a transparent service. backlog This parameter sets the queue size (maximum number) of TCP connections that are established by the kernel, but not yet accepted by Zorp. This queue stores the connections that successfully performed the three-way TCP handshake with the Zorp host, until the listener sends the Accept package. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the listener will start from the listener's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. follow_parent Set this parameter to TRUE if the listener handles also the connections coming from the child zones of the selected client zones. Otherwise, the listener accepts traffic only from the client zones explicitly listed in the services parameter. mark_tproxy Set this parameter to TRUE to mark all connections accepted by the Listener with the -m tproxy IPtables label. """ super(ZoneListener, self).__init__(convertSockAddrToDB(bindto, ZD_PROTO_TCP), services, **kw) class CSZoneListener(CSZoneDispatcher): """ Class encapsulating a Listener which selects a service based on the client and the server zone. CSZoneListeners are similar to Listeners, but select a service based on the zone of the client and the destination server. See for details. CSZoneListener example The following example defines a CSZonelistener that starts the service called internet_HTTP_DMZ for connections received on the 192.168.2.1 IP address, but only if the connection comes from the internet zone and the destination is in the DMZ zone. Listener(bindto=SockAddrInet('192.168.2.1', 50080), services={("internet", "DMZ"):"internet_HTTP_DMZ"}, transparent=TRUE, backlog=255, threaded=FALSE, follow_parent=FALSE) """ def __init__(self, bindto, services, **kw): """ Constructor to initialize a CSZoneListener instance. This constructor creates a new CSZoneListener instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Listener accepts connections. services HASH;STRING_zone,STRING_zone;STRING_service Client zone - server zone - service name pairs using the (("client_zone","server_zone"):"service") format; specifying the service to start when the listener accepts a connection from the given client zone. transparent Set this parameter to TRUE if the listener starts a transparent service. backlog This parameter sets the queue size (maximum number) of TCP connections that are established by the kernel, but not yet accepted by Zorp. This queue stores the connections that successfully performed the three-way TCP handshake with the Zorp host, until the dispatcher sends the Accept package. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the listener will start from the listener's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. follow_parent Set this parameter to TRUE if the listener handles also the connections coming from the child zones of the selected client zones. Otherwise, the listener accepts traffic only from the explicitly listed client zones. mark_tproxy Set this parameter to TRUE to mark all connections accepted by the Listener with the -m tproxy IPtables label. """ super(CSZoneListener, self).__init__(convertSockAddrToDB(bindto, ZD_PROTO_TCP), services, **kw) zorp-3.9.5/pylib/Zorp/Makefile.am000066400000000000000000000006701172670260400166370ustar00rootroot00000000000000 pkgdatadir = @datadir@/zorp/pylib/Zorp pkgdata_DATA = Auth.py AuthDB.py Core.py Chainer.py Dispatch.py \ Subnet.py Listener.py Matcher.py NAT.py Proxy.py \ Router.py Receiver.py Service.py Session.py SockAddr.py \ Stream.py Zone.py Zorp.py __init__.py \ Globals.py Cache.py Resolver.py Stack.py Config.py \ KZorp.py Notification.py Keybridge.py \ Pssl.py Util.py Rule.py Exceptions.py \ Encryption.py EXTRA_DIST = $(pkgdata_DATA) zorp-3.9.5/pylib/Zorp/Matcher.py000066400000000000000000001513471172670260400165500ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the Matchers. In general, matcher policies can be used to find out if a parameter is included in a list (or which elements of a list correspond to a certain parameter), and influence the behavior of the proxy class based on the results. Matchers can be used for a wide range of tasks, for example, to determine if the particular IP address or URL that a client is trying to access is on a black or whitelist, or to verify that a particular e-mail address is valid. """ from Zorp import * from Cache import TimedCache from Exceptions import MatcherException import os, re, string, DNS, types, time, smtplib, socket, traceback class MatcherPolicy(object): """ Class encapsulating a Matcher which can be used by a name. Matcher policies can be used to find out if a parameter is included in a list, or which elements of a list correspond to a certain parameter), and influence the behavior of the proxy class based on the results. Matchers can be used for a wide range of tasks, for example, to determine if the particular IP address or URL that a client is trying to access is on a black or whitelist, or to verify that a particular e-mail address is valid. MatcherPolicy instances are reusable matchers that contain configured instances of the matcher classes (e.g., DNSMatcher, RegexpMatcher) available in Zorp. For examples, see the specific matcher classes. """ def __init__(self, name, matcher): """ matcher The encapsulated Matcher name The name of the Matcher """ self.name = name self.matcher = matcher if Globals.matchers.has_key(name): raise ValueError, "Duplicate matcher policy: %s" % name Globals.matchers[name] = self def getMatcher(matcher_or_name_or_whatever): """ """ if isinstance(matcher_or_name_or_whatever, AbstractMatcher): return matcher_or_name_or_whatever if isinstance(matcher_or_name_or_whatever, MatcherPolicy): return matcher_or_name_or_whatever.matcher elif isinstance(matcher_or_name_or_whatever, str): if Globals.matchers.has_key(matcher_or_name_or_whatever): return Globals.matchers[matcher_or_name_or_whatever].matcher log(None, CORE_POLICY, 3, "No such matcher; matcher='%s'" % (matcher_or_name_or_whatever)) else: raise MatcherException, "Matcher is of invalid type" class AbstractMatcher(object): """ Class encapsulating the abstract string matcher. This abstract class encapsulates a string matcher that determines whether a given string is found in a backend database. Specialized subclasses of AbstractMatcher exist such as 'RegexpFileMatcher' which use regular expressions stored in flat files to find matches. """ def __init__(self): """ Constructor to initialize an AbstractMatcher instance. This constructor initializes an AbstractMatcher instance. Currently it does nothing. """ pass def checkMatch(self, str): """ Virtual function to check if a given string actually matches. This function determines if a given string actually matches with an element of a backend. It can raise a MatcherException to indicate general failure. str string to check """ raise NotImplementedError class RegexpMatcher(AbstractMatcher): """ Class encapsulating a Matcher which uses regular expressions for string matching. A simple regular expression based matcher with a match and an ignore list. Searches are case-insensitive. RegexpMatcher example The following RegexpMatcher matches only the smtp.example.com string. MatcherPolicy(name="Smtpdomains", matcher=RegexpMatcher (match_list=("smtp.example.com",), ignore_list=None)) match A list of compiled regular expressions which result in a positive match. ignore A list of compiled regular expressions defining the strings to be ignored even if match resulted in a positive match. """ def __init__(self, match_list=None, ignore_list=None, ignore_case=TRUE): """ Constructor to initialize a RegexpMatcher instance. This constructor initializes a RegexpMatcher instance by setting the match and ignore attributes to an empty list. match_list None The list of regular expressions to match. ignore_list None The list of regular expressions to ignore. """ super(RegexpMatcher, self).__init__() self.match = [] self.ignore = [] self.ignore_case = ignore_case if match_list: for x in match_list: re = self.compilePattern(x) if re: self.match.append(re) if ignore_list: for x in ignore_list: re = self.compilePattern(x) if re: self.ignore.append(re) def compilePattern(self, pat): """ """ try: if self.ignore_case == TRUE: return (re.compile(pat.rstrip(), re.IGNORECASE), pat) else: return (re.compile(pat.rstrip()), pat) except re.error: log(None, CORE_POLICY, 3, "Error compiling regular expression; expr='%s'", (pat)) def checkMatch(self, str): """ Function to determine if a given string actually matches. This function uses the attributes 'match' and 'ignore' to check if a string matches. str string to check """ for pattern in self.match: if pattern[0].search(str): for pattern_ign in self.ignore: if pattern_ign[0].search(str): return FALSE ## LOG ## # This message reports that a matching regexp pattern was found # for the given string. ## log(None, CORE_POLICY, 4, "Matching regexp found; str='%s', pattern='%s'", (str, pattern[1])) return TRUE return FALSE class RegexpFileMatcher(RegexpMatcher): """ Class encapsulating Matcher which uses regular expressions stored in files for string matching. This class is similar to RegexpMatcher, but stores the regular expressions to match and ignore in files. For example, this class can be used for URL filtering. The matcher itself stores only the paths and the filenames to the lists. Zorp automatically monitors the file and reloads it when it is modified. Searches are case-insensitive. RegexpFileMatcher example MatcherPolicy(name="demo_regexpfilematcher", matcher=RegexpFileMatcher(match_fname="/tmp/match_list.txt", ignore_fname="/tmp/ignore_list.txt")) match_file Name of the file storing the patterns for positive matches. match_date Date (in unix timestamp format) when the match_file was loaded. ignore_file Name of the file storing the patterns to ignore. ignore_date Date (in unix timestamp format) when the ignore_file was loaded. """ def __init__(self, match_fname=None, ignore_fname=None): """ Constructor to initialize a RegexpFileMatcher instance. This constructor initializes an instance of the RegexpFileMatcher class. match_fname None Name of the file storing the patterns for positive matches. ignore_fname None Name of the file storing the patterns to ignore. """ super(RegexpFileMatcher, self).__init__() self.match_file = match_fname self.match_date = 0 self.ignore_file = ignore_fname self.ignore_date = 0 def readFile(self, filename, array): """ Function to read the contents of a file to an array of regular expressions. This function is called to load a set of patterns to an array. The file is read line by line, and each line is compiled as a regular expression. filename file to read array array to place compiled regular expressions into """ f = open(filename, 'r') line = string.rstrip(f.readline()) while line: re = self.compilePattern(line) if re: array.append(re) line = string.rstrip(f.readline()) def checkMatch(self, str): """ Function to determine if a string matches. This function is part of the AbstractMatch interface, and is called when the fate of a given string is to be determined. The implementation here checks if the pattern files have been changed, loads them if necessary and decides if the given string matches. str string to check """ if self.match_file: try: st = os.stat(self.match_file) if self.match_date < st[8]: self.match_date = st[8] self.match = [] self.readFile(self.match_file, self.match) except OSError: ## LOG ## # This message indicates that the Zorp was unable to open the file containing the match regexps. # It is likely that the file does not exists or Zorp is not permitted to read it. # @see: Matcher.RegexpFileMatcher ## log(None, CORE_POLICY, 3, "Error opening match file; filename='%s'", (self.match_file,)) if self.ignore_file: try: st = os.stat(self.ignore_file) if self.ignore_date < st[8]: self.ignore_date = st[8] self.ignore = [] self.readFile(self.ignore_file, self.ignore) except OSError: ## LOG ## # This message indicates that the Zorp was unable to open the file containing the ignore regexps. # It is likely that the file does not exists or Zorp is not permitted to read it. # @see: Matcher.RegexpFileMatcher ## log(None, CORE_POLICY, 3, "Error opening ignore file; filename='%s'", (self.ignore_file,)) return super(RegexpFileMatcher, self).checkMatch(str) class CombineMatcher(AbstractMatcher): """ Matcher for implementing logical expressions based on other matchers. This matcher makes it possible to combine the results of several matchers using logical operations. CombineMatcher uses prefix-notation in its expressions and uses the following format: the operand, a comma, first argument, a comma, second argument. For example, an AND expression should be formatted the following way: (Z_AND, matcher1, matcher2). Expressions using more than one operands should be bracketed, e.g., (Z_OR (Z_AND, matcher1, matcher2), matcher3). The following oprations are available: Z_AND : Logical AND operation. Z_OR : Logical OR operation. Z_XOR : Logical XOR operation. Z_NOT : Logical negation. Z_EQ : Logical equation. Whitelisting e-mail recipients A simple use for CombineMatcher is to filter the recipients of e-mail addresses using the following process: An SmtpInvalidMatcher (called SmtpCheckrecipient) verifies that the recipient exists. A RegexpMatcher (called SmtpWhitelist) or RegexpFileMatcher is used to check if the address is on a predefined list (list of permitted addresses). A CombineMatcher (called SmtpCombineMatcher) sums up the results of the matchers with a logical AND operation. An SmtpProxy (called SmtpRecipientMatcherProxy) references SmtpCombineMatcher in its recipient_matcher attribute. Python: class SmtpRecipientMatcherProxy(SmtpProxy): recipient_matcher="SmtpCombineMatcher" def config(self): super(SmtpRecipientMatcherProxy, self).config() MatcherPolicy(name="SmtpCombineMatcher", matcher=CombineMatcher (expr=(Z_AND, "SmtpCheckrecipient", "SmtpWhitelist"))) MatcherPolicy(name="SmtpWhitelist", matcher=RegexpMatcher (match_list=("info@example.com",), ignore_list=None)) MatcherPolicy(name="SmtpCheckrecipient", matcher=SmtpInvalidRecipientMatcher (server_port=25, cache_timeout=60, attempt_delivery=FALSE, force_delivery_attempt=FALSE, server_name="recipientcheck.example.com")) op STRING The operator arg TUPLE Argument tuple, elements are instances of any descendant of AbstractMatcher. """ def __init__(self, expr): """ Constructor to initialize a CombineMatcher instance. This constructor initializes an instance of the CombineMatcher class. expr The expression tuple """ super(CombineMatcher, self).__init__() self.arg = [] # check for operator and argument count argcount = len(expr) - 1 if argcount < 0: raise MatcherException, "Missing operator in CombineMatcher expression" self.op = expr[0] if self.op == Z_NOT: if argcount != 1: raise MatcherException, "Invalid number of arguments (%d) for operator %s in CombineMatcher expression" % (argcount, self.op) elif self.op == Z_AND: if argcount < 1: raise MatcherException, "Invalid number of arguments (%d) for operator %s in CombineMatcher expression" % (argcount, self.op) elif self.op == Z_OR: if argcount < 1: raise MatcherException, "Invalid number of arguments (%d) for operator %s in CombineMatcher expression" % (argcount, self.op) elif self.op == Z_XOR: if argcount < 1: raise MatcherException, "Invalid number of arguments (%d) for operator %s in CombineMatcher expression" % (argcount, self.op) elif self.op == Z_EQ: if argcount < 1: raise MatcherException, "Invalid number of arguments (%d) for operator %s in CombineMatcher expression" % (argcount, self.op) else: raise MatcherException, "Invalid operator %s in CombineMatcher expression" % (self.op) for argidx in range(argcount): next_arg = expr[argidx + 1] if type(next_arg) == types.TupleType or type(next_arg) == types.ListType: self.arg.append(CombineMatcher(next_arg)) else: self.arg.append(getMatcher(next_arg)) def checkMatch(self, str): """ Virtual function to check if a given string actually matches. This function evaluates the arguments and makes a decision according to the operator. NOTE: early decision is used, so in cases AND(FALSE, X) and OR(TRUE, X) the matcher X won't be evaluated at all. It can raise a MatcherException to indicate general failure. """ # 1st operand must be evaluated anyway if self.op == Z_NOT: return not self.arg[0].checkMatch(str) elif self.op == Z_AND: for match in self.arg: if not match.checkMatch(str): return FALSE return TRUE elif self.op == Z_OR: for match in self.arg: if match.checkMatch(str): return TRUE return FALSE elif self.op == Z_XOR: res = FALSE for match in self.arg: res = res != match.checkMatch(str) return res elif self.op == Z_EQ: res = None for match in self.arg: if not res: res = match.checkMatch(str) elif res != match.checkMatch(str): return FALSE return TRUE else: raise MatcherException, "Invalid operator %s in evaluating CombineMatcher" % (self.op) return res class DNSMatcher(AbstractMatcher): """ DNS matcher DNSMatcher retrieves the IP addresses of domain names. This can be used in domain name based policy decisions, for example to allow encrypted connections only to trusted e-banking sites. DNSMatcher operates as follows: it resolves the IP addresses stored in the list of domain names using the specified Domain Name Server, and compares the results to the IP address of the connection (i.e., the IP address of the server or the client). The matcher returns a true value if the IP addresses resolved from the list of domain names include the IP address of the connection. DNSMatcher example The following DNSMatcher class uses the dns.example.com name server to resolve the example2.com and example3.com domain names. MatcherPolicy(name="ExampleDomainMatcher", matcher=DNSMatcher(server="dns.example.com", hosts=("example2.com", "example3.com"))) """ DNS.DiscoverNameServers() def __init__(self, hosts, server=None): """ Constructor to initialize an instance of the DNSMatcher class. This constructor initializes an instance of the DNSMatcher class. hosts Hostnames to resolve. server None IP address of the DNS server to query. Defaults to the servers set in the resolv.conf file. """ self.server = server if type(hosts) == type(''): self.hosts = [ hosts ] else: self.hosts = hosts # address -> set of names self.cache_address_to_names = {} # name -> set of addresses self.cache_name_to_addresses = {} # hostname -> expiry timestamp self.expires = {} def addHostToCache(self, host, addresses): """ """ self.cache_name_to_addresses.setdefault(host, set()).update(set(addresses)) for address in addresses: self.cache_address_to_names.setdefault(address, set()).add(host) def dropHostFromCache(self, host): """ """ if host not in self.cache_name_to_addresses: return for address in self.cache_name_to_addresses[host]: self.cache_address_to_names[address].remove(host) if len(self.cache_address_to_names[address]) == 0: del self.cache_address_to_names[address] del self.cache_name_to_addresses[host] def updateCachedHost(self, host, now): """ """ # drop old cached values self.dropHostFromCache(host) # do lookup params = { "name": host, "qtype": DNS.Type.ANY } if self.server: params["server"] = self.server request = DNS.DnsRequest(**params) try: answer = request.req() except DNSError: log(None, CORE_ERROR, 3, "Error resolving host; host='%s'", host) return if len(answer.answers) > 0: # filter A and AAAA records addresses = filter(lambda x: x["typename"] == "A" or x["typename"] == "AAAA", answer.answers) # got A or AAA records, find minimum TTL and update cache ttl = min(map(lambda x: x["ttl"], addresses) ) # IPv4 addresses are already in a string form in the data attribute self.addHostToCache(host, map(lambda x: x["data"], filter(lambda y: y["typename"] == "A", addresses ))) # while IPv6 addresses are represented in their network representation, need to be converted self.addHostToCache(host, map(lambda x: socket.inet_ntop(socket.AF_INET6, x["data"]), filter(lambda y: y["typename"] == "AAAA", addresses))) else: # no records, cache failure for negative TTL secs ttl = answer.authority[0]["data"][6][1] log(None, CORE_DEBUG, 6, "No such host found; host='%s'", host) # set expiry self.expires[host] = now + ttl def updateCache(self, now): """ """ for host in self.hosts: if now < self.expires.get(host, -1.0): log(None, CORE_DEBUG, 6, "Host already in DNS matcher cache and within TTL; host='%s'", host) else: log(None, CORE_INFO, 5, "Host not in DNS matcher cache or has expired, doing lookup; host='%s'", host) self.updateCachedHost(host, now) def checkMatch(self, str): """ """ self.updateCache(time.time()) if self.cache_address_to_names.has_key(str): return TRUE else: return FALSE class WindowsUpdateMatcher(DNSMatcher): """ Windows Update matcher WindowsUpdateMatcher is actually a DNSMatcher used to retrieve the IP addresses currently associated with the v5.windowsupdate.microsoft.nsatc.net, v4.windowsupdate.microsoft.nsatc.net, and update.microsoft.nsatc.net domain names from the specified name server. Windows Update is running on a distributed server farm, using the DNS round robin method and a short TTL to constantly change the set of servers currently visible, consequently the IP addresses of the servers are constantly changing. WindowsUpdateMatcher example MatcherPolicy(name="demo_windowsupdatematcher", matcher=WindowsUpdateMatcher()) """ def __init__(self, server=None): """ Constructor to initialize an instance of the WindowsUpdateMatcher class. This constructor initializes an instance of the WindowsUpdateMatcher class. server None The IP address of the name server to query. """ super(WindowsUpdateMatcher, self).__init__( ['v5.windowsupdate.microsoft.nsatc.net', 'v4.windowsupdate.microsoft.nsatc.net', 'update.microsoft.com.nsatc.net'], server) class SmtpProto(smtplib.SMTP): """ """ def __init__(self, host = '', port = 0, local_hostname = None, bind_addr = ''): self.bind_addr = bind_addr smtplib.SMTP.__init__(self, host, port, local_hostname) def connect(self, host='localhost', port = 0): """ """ msg = "Error resolving hostname" for addr in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = addr try: self.sock = socket.socket(af, socktype, proto) if self.bind_addr: self.sock.bind((self.bind_addr, 0)) self.sock.connect(sa) # success break except socket.error, msg: if self.sock: self.sock.close() self.sock = None if not self.sock: raise socket.error, msg (code, msg) = self.getreply() return (code, msg) class SmtpInvalidRecipientMatcher(AbstractMatcher): """ Class verifying the validity of the recipient addresses in E-mails. This class encapsulates a VRFY/RCPT based validity checker to transparently verify the existance of E-mail addresses. Instead of immediately sending the e-mail to the recipient SMTP server, Zorp queuries an independent SMTP server about the existance of the recipient e-mail address. Instances of this class can be referred to in the recipient_matcher attribute of the SmtpProxy class. The SmtpProxy will automatically reject unknown recipients even if the recipient SMTP server would accept them. SmtpInvalidMatcher example Python: class SmtpRecipientMatcherProxy(SmtpProxy): recipient_matcher="SmtpCheckrecipient" def config(self): super(SmtpRecipientMatcherProxy, self).config() MatcherPolicy(name="SmtpCheckrecipient", matcher=SmtpInvalidRecipientMatcher (server_port=25, cache_timeout=60, attempt_delivery=FALSE, force_delivery_attempt=FALSE, server_name="recipientcheck.example.com")) """ def __init__(self, server_name, server_port=25, cache_timeout=60, attempt_delivery=FALSE, force_delivery_attempt=FALSE, sender_address='<>', bind_name=''): """ server_name Domain name of the SMTP server that will verify the addresses. server_port 25 Port of the target server. cache_timeout 60 How long will the result of an address verification be retained (in seconds). attempt_delivery FALSE Obsolete, ignored. force_delivery_attempt FALSE Force a delivery attempt even if the autodetection code otherwise would use VRFY. Useful if the server always returns success for VRFY. sender_address "<>" This value will be used as the mail sender for the attempted mail delivery. Mail delivery is attempted if the force_delivery_attempt is TRUE, or the recipient server does not support the VRFY command. bind_name "" Specifies the hostname to bind to before initiating the connection to the SMTP server. """ super(SmtpInvalidRecipientMatcher, self).__init__() self.force_delivery_attempt = force_delivery_attempt self.server_name = server_name self.server_port = server_port self.bind_name = bind_name self.sender_address = sender_address self.cache = TimedCache('smtp_valid_recipients(%s)' % server_name, cache_timeout) def checkMatch(self, email): """ """ # email is a fully qualified email address like address@domain.com try: cached = self.cache.lookup(email) if cached != None: ## LOG ## # This message reports that the recipient address has been already checked and # Zorp uses the cached information. ## log(None, CORE_DEBUG, 6, "Cached recipient match found; email='%s', cached='%d'", (email, cached)) if cached: return TRUE else: return FALSE except KeyError: cached = None try: ## LOG ## # This message reports that the recipient address has not been already checked and # Zorp is going to check it now directly. ## log(None, CORE_DEBUG, 6, "Recipient validity not cached, trying the direct way; email='%s'", (email)) server = SmtpProto(self.server_name, self.server_port, bind_addr=self.bind_name) try: (smtp_code, smtp_msg) = server.ehlo() if smtp_code > 299: (smtp_code, smtp_msg) = server.helo() esmtp = FALSE else: esmtp = TRUE if smtp_code > 299: raise MatcherException, "Server refused our EHLO/HELO command." present = FALSE smtp_code = -1 if not self.force_delivery_attempt and (not esmtp or server.has_extn("VRFY")): log(None, CORE_DEBUG, 6, "Trying to use VRFY to check email address validity; email='%s'", (email,)) (smtp_code, smtp_msg) = server.verify(email) present = (smtp_code < 300) else: log(None, CORE_DEBUG, 6, "Attempting delivery to check email address validity; email='%s'", (email,)) (smtp_code, smtp_msg) = server.mail(self.sender_address) if smtp_code == 250: (smtp_code, smtp_msg) = server.rcpt(email) present = (smtp_code < 300) else: ## LOG ## # This message indicates that the sender address was rejected during the recipient address # verify check and Zorp rejects the recipient address. ## log(None, CORE_ERROR, 3, "SMTP sender was rejected, unable to verify user existence; email='%s', server_address='%s', server_port='%d'", (email, self.server_name, self.server_port)) raise MatcherException, "Server has not accepted our sender (%s)." % self.sender_address if present: ## LOG ## # This message reports that the recipient address verify was successful and Zorp accepts it. ## log(None, CORE_INFO, 5, "Server accepted recipient; email='%s'", email) # we only cache successful lookups self.cache.store(email, not present) elif smtp_code != -1: ## LOG ## # This message reports that the recipient address verify was unsuccessful and Zorp rejects it. ## log(None, CORE_INFO, 4, "Server rejected recipient; email='%s'", email) finally: server.quit() except (socket.error, smtplib.SMTPException), e: ## LOG ## # This message indicates that an SMTP error occurred during the recipient address verify # and Zorp rejects it. ## log(None, CORE_ERROR, 3, "SMTP error during recipient validity checking; info='%s'", e) raise MatcherException, "SMTP error or socket failure while checking user validity (%s)" % str(e) # we return when we want to reject... return not present zorp-3.9.5/pylib/Zorp/NAT.py000066400000000000000000000766641172670260400156170ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to Network Address Translation. Network Address Translation (NAT) is a technology that can be used to change source or destination addresses in a connection from one IP address to another one. This module defines the classes performing the translation for IP addresses. Zorp supports several different NAT methods using different NAT classes, like GeneralNAT or StaticNAT. To actually perform network address translation in a service, you have to use a NATPolicy instance that contains a configured NAT class. NAT policies provide a way to re-use NAT instances whithout having to define NAT mappings for each service individually. NAT_SNAT NAT_DNAT """ from Zorp import * from SockAddr import SockAddrInet from Subnet import InetSubnet from Cache import ShiftCache, TimedCache from Exceptions import DACException, LimitException from socket import inet_ntoa import Globals import types from random import choice, randint, SystemRandom import kznf.kznfnetlink import socket NAT_SNAT = 0 NAT_DNAT = 1 Globals.nat_policies[None] = None class NATPolicy(object): """ Class encapsulating named NAT instances. This class encapsulates a name and an associated NAT instance. NAT policies provide a way to re-use NAT instances whithout having to define NAT mappings for each service individually. Using Natpolicies The following example defines a simple NAT policy, and uses this policy for SNAT in a service. NATPolicy(name="demo_natpolicy", nat=GeneralNAT(mapping=((InetDomain(addr="10.0.1.0/24"), InetDomain(addr="192.168.1.0/24")),))) Service(name="office_http_inter", proxy_class=HttpProxy, snat_policy="demo_natpolicy") """ def __init__(self, name, nat, cacheable=TRUE): """ Constructor to initialize a NAT policy. This contructor initializes a NAT policy. name Name identifying the NAT policy. nat NAT object which performs address translation. cacheable TRUE Enable this parameter to cache the NAT decisions. """ self.name = name self.nat = nat self.cacheable = cacheable if self.cacheable: self.nat_cache = ShiftCache('nat(%s)' % name, 1000) if Globals.nat_policies.has_key(name): raise ValueError, "Duplicate NATPolicy name: %s" % name Globals.nat_policies[name] = self def performTranslation(self, session, addrs, nat_type): """ """ if session: session_id = session.session_id else: session_id = None ## LOG ## # This message reports that the NAT type and the old address before the NAT mapping occurs. ## log(session_id, CORE_DEBUG, 4, "Before NAT mapping; nat_type='%d', src_addr='%s', dst_addr='%s'", (nat_type, str(addrs[NAT_SNAT]), str(addrs[NAT_DNAT]))) if self.cacheable: if addrs[NAT_SNAT] and addrs[NAT_DNAT]: key = (addrs[NAT_SNAT].ip_s, addrs[NAT_DNAT].ip_s) elif addrs[NAT_SNAT]: key = (addrs[NAT_SNAT].ip_s, None) elif addrs[NAT_DNAT]: key = (None, addrs[NAT_DNAT].ip_s) else: raise ValueError, "NAT without any address set" cached = self.nat_cache.lookup(key) if cached: addr = addrs[nat_type].clone(FALSE) addr.ip_s = cached else: addr = self.nat.performTranslation(session, addrs, nat_type) self.nat_cache.store(key, addr.ip_s) else: addr = self.nat.performTranslation(session, addrs, nat_type) ## LOG ## # This message reports that the NAT type and the new address after the NAT mapping occurred. ## log(session_id, CORE_DEBUG, 4, "After NAT mapping; nat_type='%d', src_addr='%s', dst_addr='%s', new_addr='%s'", (nat_type, str(addrs[NAT_SNAT]), str(addrs[NAT_DNAT]), str(addr))) return addr def getKZorpMapping(self): """ """ if hasattr(self.nat, 'getKZorpMapping'): return self.nat.getKZorpMapping() raise ValueError, "NAT class does not support KZorp representation" def getNATPolicy(name): """ """ if name: if Globals.nat_policies.has_key(name): return Globals.nat_policies[name] else: log(None, CORE_POLICY, 3, "No such NAT policy; policy='%s'", name) return None class AbstractNAT(object): """ Class encapsulating the abstract NAT interface. This class encapsulates an interface for application level network address translation (NAT). This NAT is different from the NAT used by packet filters: it modifies the outgoing source/destination addresses just before Zorp connects to the server. Source and destination NATs can be specified when a Service is created. The NAT settings are used by the ConnectChainer class just before connecting to the server. """ def __init__(self): """ Constructor to initialize an AbstractNAT instance. This constructor initializes an AbstractNAT instance. Currently it does nothing, but serves as a placeholder for future extensions. """ pass def performTranslation(self, session, addrs, nat_type): """ Function that performs the address translation. This function is called before connecting a session to the destination server. The function returns the address (a SockAddr instance) to bind to before establishing the connection. session Session which is about to connect the server. addrs tuple of (source, destination) address, any of them can be none in case of the other translation nat_type translation type, either NAT_SNAT or NAT_DNAT """ raise NotImplementedError class GeneralNAT(AbstractNAT): """ Class encapsulating a general subnet-to-subnet NAT. This class encapsulates a general subnet-to-subnet NAT. It requires a list of from, to, translated to parameters: from: the source address of the connection. to: the destination address of the connection. translated to: the translated address. If the NAT policy is used as SNAT, the translated address is used to translate the source address of the connection; if the NAT policy is used as DNAT, the translated address is used to translate the destination address of the connection. The translation occurs according to the first matching rule. GeneralNat example The following example defines a simple GeneralNAT policy that maps connections coming from the 192.168.1.0/24 subnet and targeting the 192.168.10.0/24 subnet into the 10.70.0.0/24 subnet. NATPolicy(name="Demo_GeneralNAT", nat=GeneralNAT(mapping=((InetDomain(addr="192.168.1.0/24"), InetDomain(addr="192.168.10.0/24"), InetDomain(addr="10.70.0.0/24")),))) If the policy is used as SNAT, the 192.168.1.0/24 subnet is translated into the 10.70.0.0/24 subnet and used as the source address of the connection. If the policy is used as DNAT, the 192.168.10.0/24 subnet is translated into the 10.70.0.0/24 subnet and used as the target address of the connection. """ def __init__(self, mapping): """ Constructor to initialize a GeneralNAT instance. This constructor initializes a GeneralNAT instance. mapping List of tuples of InetDomains in (source domain, destination domain, mapped domain) format. """ super(GeneralNAT, self).__init__() if type(mapping) != types.TupleType and type(mapping) != types.ListType: mapping = (mapping,) self.mappings = [[], []] for map in mapping: if len(map) == 2: raise ValueError, "GeneralNAT with old-style mapping parameter is not supported" else: self.mappings[NAT_SNAT].append(map) self.mappings[NAT_DNAT].append(map) def performTranslation(self, session, addrs, nat_type): """ """ for map in self.mappings[nat_type]: (src_dom, dst_dom, map_dom) = map if ((not addrs[NAT_SNAT] or src_dom.contains(addrs[NAT_SNAT])) and (not addrs[NAT_DNAT] or dst_dom.contains(addrs[NAT_DNAT]))): # we have a match is in domain, do translation addr = addrs[nat_type].clone(FALSE) hostaddr = map[nat_type].getHostAddr(addrs[nat_type]) addr.ip = map_dom.mapHostAddr(hostaddr) return addr return addrs[nat_type] def getKZorpMapping(self): """ """ def domainToKZorpTuple(domain): return (kznf.kznfnetlink.KZ_SVC_NAT_MAP_IPS, socket.ntohl(domain.netaddr()), socket.ntohl(domain.broadcast()), 0, 0) result = [] for (src_dom, dst_dom, map_dom) in self.mappings[NAT_SNAT]: result.append((domainToKZorpTuple(src_dom), domainToKZorpTuple(dst_dom), domainToKZorpTuple(map_dom))) return result class ForgeClientSourceNAT(AbstractNAT): """ Class using the original client address for outgoing connections. This class uses the client's IP address as the source address of the server-side connection. That way the server sees that the connection comes from the original client instead of the firewall. This feature is useful when the source address of the server-side conneciton is important, for example, to webservers performing address-based access control. This class is OBSOLETE and may be removed in future releases. Use the forge_addr parameter of the Router class used in the service definition instead. """ def performTranslation(self, session, addrs, nat_type): """ """ return session.client_address.clone(TRUE) class StaticNAT(AbstractNAT): """ Class that replaces the source or destination address with a predefined address. This class assigns a predefined value to the address of the connection. """ def __init__(self, addr): """ Constructor to initialize a StaticNAT instance. This constructor initializes a StaticNAT instance. addr The address that replaces all addresses. """ super(StaticNAT, self).__init__() self.addr = addr def performTranslation(self, session, addrs, nat_type): """ """ return self.addr.clone(FALSE) class OneToOneNAT(AbstractNAT): """ Class translating addresses between two IP ranges. This class is obsolete, use GeneralNAT instead. This class performs 1:1 address translation between the source and destination subnets. If the source address is outside the given source address range, a DACException is raised. The source and destination subnets must have the same size. Use OneToOneNAT to redirect a a block of IP addresses to another block, for example, when the webservers located in the DMZ have dedicated IP aliases on the firewall. """ def __init__(self, from_domain, to_domain, default_reject=TRUE): """ Constructor to initialize a OneToOneNAT instance. This constructor initializes a OneToOneNAT instance. Arguments must be InetDomain instances specifying two non-overlapping IP subnets with the same size. from_domain The source subnet (InetDomain instance). to_domain The destination subnet (InetDomain instance). default_reject TRUE Enable this parameter to reject all connections outside the specific source range. """ super(OneToOneNAT, self).__init__() self.from_domain = from_domain self.to_domain = to_domain self.default_reject = default_reject if from_domain.netmaskbits() != to_domain.netmaskbits(): raise ValueError, 'OneToOneNAT requires two domains of the same size' def performTranslation(self, session, addrs, nat_type): """ """ try: return self.mapAddress(addrs[nat_type], self.from_domain, self.to_domain, nat_type) except ValueError: pass if self.default_reject: raise DACException, 'IP not within the required range.' else: return addr def mapAddress(self, addr, from_domain, to_domain, nat_type): """ Function to map an address to another subnet. This function maps the address 'addr' in the domain 'from_domain' to another domain 'to_domain'. Returns a SockAddrInet in the destination domain or None from_domain source domain to_domain destination domain nat_type specifies the NAT type """ if addr < from_domain: ip = (addr.ip & ~to_domain.netmask()) + (to_domain.netaddr() & to_domain.netmask()) if nat_type == NAT_SNAT: return SockAddrInet(inet_ntoa(ip), 0) elif nat_type == NAT_DNAT: return SockAddrInet(inet_ntoa(ip), addr.port) class OneToOneMultiNAT(OneToOneNAT): """ Class translating addresses between two IP ranges. This class is obsolete, use GeneralNAT instead. This class is similar to OneToOneNAT as it 1:1 address translation between the source and destination subnets. The difference is that the OneToOneMultiNAT class supports multiple mappings by using a list of mapping pairs. If the source address is outside the given source address range, a DACException is raised. The source and destination subnets must have the same size. """ def __init__(self, mapping, default_reject=TRUE): """ Constructor to initialize a OneToOneMultiNAT instance. This constructor initializes an instance of the OneToOneMultiNAT class. Arguments must be InetDomain instances specifying two non-overlapping IP subnets with the same size. mapping List of InetDomain pairs in the from, to format. default_reject TRUE Enable this parameter to reject all connections outside the specific source range. """ super(OneToOneMultiNAT, self).__init__() self.mapping = mapping self.default_reject = default_reject for (from_domain, to_domain) in mapping: if from_domain.netmaskbits() != to_domain.netmaskbits(): raise ValueError, 'OneToOneMultiNAT requires two domains of the same size' def performTranslation(self, session, addrs, nat_type): """ """ for (from_domain, to_domain) in self.mapping: try: return self.mapAddress(addrs[nat_type], from_domain, to_domain, nat_type) except ValueError: pass if self.default_reject: raise DACException, 'IP not within the required range.' else: return addr class RandomNAT(AbstractNAT): """ Class generating a random IP address. This class randomly selects an address from a list of IP addresses. This can be used for load-balancing several lines by binding each session to a different interface. """ def __init__(self, addresses): """ Constructor to initialize a RandomNAT instance. This constructor initializes a RandomNAT instance. addresses List of the available interfaces. Each item of the list must be am instance of the SockAddr (or a derived) class. """ super(RandomNAT, self).__init__() self.addresses = addresses def performTranslation(self, session, addrs, nat_type): """ """ return choice(self.addresses) class HashNAT(AbstractNAT): """ Class which sets the address from a hash table. HashNAT statically maps an IP address to another using a hash table. The table is indexed by the source IP address, and the value is the translated IP address. Both IP addresses are stored in string format. """ def __init__(self, ip_hash, default_reject=TRUE): """ Constructor to initialize a HashNAT instance. This constructor initializes a HashNAT instance. ip_hash The hash storing the IP address. default_reject TRUE Enable this parameter to reject all connections outside the specific source range. """ super(HashNAT, self).__init__() self.ip_hash = ip_hash self.default_reject = default_reject def performTranslation(self, session, addrs, nat_type): """ """ try: ip = self.ip_hash[addrs[nat_type].ip_s] if nat_type == NAT_SNAT: return SockAddrInet(ip, 0) else: return SockAddrInet(ip, addr.port) except KeyError: if self.default_reject: raise DACException, 'IP not within the required range.' else: return addr zorp-3.9.5/pylib/Zorp/Notification.py000066400000000000000000000021441172670260400176010ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to e-mail and other notifications. """ zorp-3.9.5/pylib/Zorp/Proxy.py000066400000000000000000001276771172670260400163170ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Proxy module defines the abstract proxy class. This module encapsulates the ZorpProxy component implemented by the Zorp core. The Proxy module provides a common framework for protocol-specific proxies, implementing the functions that are used by all proxies. Protocol-specific proxy modules are derived from the Proxy module, and are described in . """ from Zorp import * from Stream import Stream from SockAddr import SockAddrInet from Session import StackedSession, MasterSession from Stack import getStackingProviderBackend from Keybridge import * from Chainer import ConnectChainer from Exceptions import * import string, os, sys, traceback, re, types SSL_ERROR = 'core.error' SSL_DEBUG = 'core.debug' SSL_INFO = 'core.info' SSL_VIOLATION = 'core.violation' SSL_VERIFY_NONE = 0 SSL_VERIFY_OPTIONAL = 1 SSL_VERIFY_OPTIONAL_UNTRUSTED = 1 SSL_VERIFY_OPTIONAL_TRUSTED = 2 SSL_VERIFY_REQUIRED_UNTRUSTED = 3 SSL_VERIFY_REQUIRED_TRUSTED = 4 # handshake order SSL_HSO_CLIENT_SERVER = 0 SSL_HSO_SERVER_CLIENT = 1 # handshake policy decisions SSL_HS_ACCEPT = 1 SSL_HS_REJECT = 3 SSL_HS_POLICY = 6 SSL_HS_VERIFIED = 10 SSL_METHOD_SSLV23 = "SSLv23" SSL_METHOD_SSLV2 = "SSLv2" SSL_METHOD_SSLV3 = "SSLv3" SSL_METHOD_TLSV1 = "TLSv1" SSL_METHOD_ALL = "SSLv23" SSL_CIPHERS_ALL = "ALL:!aNULL:@STRENGTH" SSL_CIPHERS_HIGH = "HIGH:!aNULL:@STRENGTH" SSL_CIPHERS_MEDIUM = "HIGH:MEDIUM:!aNULL:@STRENGTH" SSL_CIPHERS_LOW = "HIGH:MEDIUM:LOW:EXPORT:!aNULL:@STRENGTH" # connection security settings SSL_NONE = 0 SSL_FORCE_SSL = 1 SSL_ACCEPT_STARTTLS = 2 SSL_FORWARD_STARTTLS = 3 def proxyLog(self, type, level, msg, args=None): """ Function to send a proxy-specific message to the system log. This function sends a message into the system log. All messages start with the session_id that uniquely identifies the connection. type The class of the log message. level Verbosity level of the log message. msg The text of the log message. """ ## NOLOG ## log(self.session.session_id, type, level, msg, args) class Proxy(BuiltinProxy): """ Class encapsulating the abstract Zorp proxy. This class serves as the abstact base class for all proxies implemented in Zorp. When an instance of the Proxy class is created, it loads and starts a protocol-specific proxy. Proxies operate in their own threads, so this constructor returns immediately. session Session instance The session inspected by the proxy. name The protocol-specific proxy class inspecting the traffic. auth_inband_defer FALSE Set this parameter to TRUE to enable the protocol-specific proxy to perform inband authentication. This has effect only if the AuthenticationPolicy used in the service requests InbandAuthentication. language "en" Determines the language used for user-visible error messages. Supported languages: en - English; de - German; hu - Hungarian. """ name = None module = None auth_inband_defer = FALSE auth_inband_supported = FALSE auth_server_supported = FALSE def __init__(self, session): """ Constructor to initialize an instance of the Proxy class. This constructor creates a new Proxy instance which creates an instance of the protocol-specific proxy class. name The protocol-specific proxy class inspecting the traffic. session SESSION The session inspected by the proxy. """ # NOTE: circular reference, it is resolved in the __destroy__ method self.session = session self.session.proxy = self setattr(self.session, self.name, self) self.server_fd_picked = FALSE self.proxy_started = FALSE session.setProxy(self.name) ## LOG ## # This message reports that a new proxy instance was started. ## log(session.session_id, CORE_SESSION, 5, "Proxy starting; class='%s', proxy='%s'", (self.__class__.__name__, self.name)) if session.owner: parent = session.owner.proxy else: parent = None if not self.module: self.module = self.name super(Proxy, self).__init__(self.name, self.module, session.session_id, session.client_stream, parent) def __del__(self): """ Destructor to deinitialize a Proxy instance. This destructor is called when this object instance is freed. It simply sends a message about this event to the log. """ ## LOG ## # This message reports that this proxy instance was ended. ## log(self.session.session_id, CORE_SESSION, 5, "Proxy ending; class='%s', module='%s'", (self.__class__.__name__, self.name)) def __pre_startup__(self): """ """ pass def __pre_config__(self): """ Function called by the proxy core to perform internal proxy initialization. This function is similar to config() to perform initialization of internal proxy related data. It is not meant as a user interface, currently it is used to perform outband authentication. """ if not self.session.auth_user and self.session.service.authentication_policy: self.session.service.authentication_policy.performAuthentication(self.session) # hack: decrease timeout for UDP sessions if (self.session.protocol == ZD_PROTO_UDP) and self.timeout > 60000: self.timeout = 60000 self.language = config.options.language self.ssl.client_trusted_certs_directory = '' self.ssl.server_trusted_certs_directory = '' self.ssl.client_keypair_generate = FALSE self.ssl.server_keypair_generate = FALSE self.ssl.client_certificate_trusted = FALSE self.ssl.server_certificate_trusted = FALSE self.ssl.client_handshake["verify_cert_ext"] = (SSL_HS_POLICY, self.verifyTrustedCertClient) self.ssl.server_handshake["verify_cert_ext"] = (SSL_HS_POLICY, self.verifyTrustedCertServer) if self.session.service.encryption_policy: self.session.service.encryption_policy.apply(self) def __post_config__(self): """ """ if self.ssl.client_keypair_generate == TRUE and self.ssl.server_keypair_generate == TRUE: raise ValueError, 'client_keypair_generate and server_keypair_generate are both enabled. '\ 'Key generation cannot work on both sides at the same time.' if self.ssl.client_connection_security > SSL_NONE: if hasattr(self.ssl, "client_cert") and type(self.ssl.client_cert) == types.StringType: self.ssl.client_cert_file = self.ssl.client_cert if hasattr(self.ssl, "client_key") and type(self.ssl.client_key) == types.StringType: self.ssl.client_key_file = self.ssl.client_key if hasattr(self.ssl, "client_keypair_files"): self.ssl.client_cert_file = self.ssl.client_keypair_files[0] self.ssl.client_key_file = self.ssl.client_keypair_files[1] if hasattr(self.ssl, "client_cagroup_directories"): self.ssl.client_ca_directory = self.ssl.client_cagroup_directories[0] self.ssl.client_crl_directory = self.ssl.client_cagroup_directories[1] if hasattr(self.ssl, "client_verify_cagroup_directories"): self.ssl.client_verify_ca_directory = self.ssl.client_verify_cagroup_directories[0] self.ssl.client_verify_crl_directory = self.ssl.client_verify_cagroup_directories[1] if hasattr(self.ssl, "client_cert_file"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing client_cert_file; value='%s'" % self.ssl.client_cert_file) self.ssl.client_local_certificate = self.readPEM(self.ssl.client_cert_file) if hasattr(self.ssl, "client_key_file"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing client_key_file; value='%s'" % self.ssl.client_key_file) self.ssl.client_local_privatekey = self.readPEM(self.ssl.client_key_file) if hasattr(self.ssl, "client_ca_directory"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing client_ca_directory; value='%s'" % self.ssl.client_ca_directory) self.readHashDir(self.ssl.client_local_ca_list, self.ssl.client_ca_directory) if hasattr(self.ssl, "client_crl_directory"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing client_crl_directory; value='%s'" % self.ssl.client_crl_directory) self.readHashDir(self.ssl.client_local_crl_list, self.ssl.client_crl_directory) if self.ssl.client_keypair_generate: if self.ssl.handshake_seq != SSL_HSO_SERVER_CLIENT: raise ValueError, "For client-side keypair generation, the handshake order"\ " must be SSL_HSO_SERVER_CLIENT." else: self.ssl.client_handshake["setup_key"] = (SSL_HS_POLICY, self.generateKeyClient) if self.ssl.server_connection_security > SSL_NONE: if hasattr(self.ssl, "server_cert") and type(self.ssl.server_cert) == types.StringType: self.ssl.server_cert_file = self.ssl.server_cert if hasattr(self.ssl, "server_key") and type(self.ssl.server_key) == types.StringType: self.ssl.server_key_file = self.ssl.server_key if hasattr(self.ssl, "server_keypair_files"): self.ssl.server_cert_file = self.ssl.server_keypair_files[0] self.ssl.server_key_file = self.ssl.server_keypair_files[1] if hasattr(self.ssl, "server_cagroup_directories"): self.ssl.server_ca_directory = self.ssl.server_cagroup_directories[0] self.ssl.server_crl_directory = self.ssl.server_cagroup_directories[1] if hasattr(self.ssl, "server_verify_cagroup_directories"): self.ssl.server_verify_ca_directory = self.ssl.server_verify_cagroup_directories[0] self.ssl.server_verify_crl_directory = self.ssl.server_verify_cagroup_directories[1] if hasattr(self.ssl, "server_cert_file"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing server_cert_file; value='%s'" % self.ssl.server_cert_file) self.ssl.server_local_certificate = self.readPEM(self.ssl.server_cert_file) if hasattr(self.ssl, "server_key_file"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing server_key_file; value='%s'" % self.ssl.server_key_file) self.ssl.server_local_privatekey = self.readPEM(self.ssl.server_key_file) if hasattr(self.ssl, "server_ca_directory"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing server_ca_directory; value='%s'" % self.ssl.server_ca_directory) self.readHashDir(self.ssl.server_local_ca_list, self.ssl.server_ca_directory) if hasattr(self.ssl, "server_crl_directory"): proxyLog(self, SSL_DEBUG, 6, "Compatibility feature, processing server_crl_directory; value='%s'" % self.ssl.server_crl_directory) self.readHashDir(self.ssl.server_local_crl_list, self.ssl.server_crl_directory) if self.ssl.server_keypair_generate: if self.ssl.handshake_seq != SSL_HSO_CLIENT_SERVER: raise ValueError, "For server-side keypair generation, the handshake order"\ " must be SSL_HSO_CLIENT_SERVER." else: self.ssl.server_handshake["setup_key"] = (SSL_HS_POLICY, self.generateKeyServer) def config(self): """ Function called by the proxy core to initialize the proxy instance. This function is called during proxy startup. It sets the attributes of the proxy instance according to the configuration of the proxy. """ pass def __destroy__(self): """ Function called by the proxy core when the session is to be freed. This function is called when the proxy module is to be freed. It simply sends a message about this event to the log. """ # NOTE: if C proxy was started but the chaining process was # not completed then the server side of the connection is # still hanging there unpicked. Close it. if self.proxy_started and self.session.server_stream and not self.server_fd_picked: self.session.server_stream.close() # free circular reference between session & proxy session = self.session del self.session.proxy delattr(self.session, self.name) ## LOG ## # This message reports that this proxy instance was destroyed and freed. ## log(self.session.session_id, CORE_DEBUG, 6, "Proxy destroy; class='%s', module='%s'", (self.__class__.__name__, self.name)) # free possible circular references in __dict__ by removing all elements self.__dict__.clear() self.session = session def stackProxy(self, client_stream, server_stream, proxy_class, stack_info, side_stacking=False): """ Function to embed (stack) a proxy into the current proxy instance. This function stacks a new proxy into the current proxy instance. The function receives the downstream filedescriptors and the protocol-specific proxy class to embed. The way the underlying proxy decides which proxy_class to use is proxy specific. client_stream The client-side data stream. server_stream The server-side data stream. proxy_class The protocol-specific proxy class to embed into the current proxy instance. side_stacking TRUE if a side-stack is requested, FALSE for normal stack. """ if side_stacking: ## LOG ## # This message reports that Zorp is about to stack a new proxy under the current proxy, as a child proxy. ## proxyLog(self, CORE_DEBUG, 7, "Stacking child proxy on right side; client_fd='%d', class='%s'", (client_stream.fd, proxy_class.__name__)) subsession = StackedSession(self.session, ConnectChainer()) else: ## LOG ## # This message reports that Zorp is about to stack a new proxy under the current proxy, as a child proxy. ## proxyLog(self, CORE_DEBUG, 7, "Stacking child proxy; client_fd='%d', server_fd='%d', class='%s'", (client_stream.fd, server_stream.fd, proxy_class.__name__)) subsession = StackedSession(self.session) subsession.stack_info = stack_info session_id = string.split(self.session.session_id, '/') if len(session_id): session_id[len(session_id)-1] = proxy_class.name session_id = string.join(session_id, '/') else: # hmm, funny session_id ... session_id = self.session.session_id subsession.client_stream = client_stream subsession.client_stream.name = "%s/client_upstream" % (session_id) if not side_stacking: subsession.server_stream = server_stream subsession.server_stream.name = "%s/server_upstream" % (session_id) try: proxy = proxy_class(subsession) if ProxyGroup(1).start(proxy): return proxy else: raise RuntimeError, "Error starting proxy in group" except: ## LOG ## # This message indicates that an error occurred during child proxy stacking. # The stacking failed and the subsession is destroyed. ## proxyLog(self, CORE_ERROR, 2, "Error while stacking child proxy; error='%s', error_desc='%s', " % (sys.exc_info()[0], sys.exc_info()[1])) subsession.destroy() raise return None def stackCustom(self, args): """ Function to perform custom stacking. This function is called by the underlying C proxy to stack a Stackin Provider (Z_STACK_PROVIDER), or to perform a customized stacking (Z_STACK_CUSTOM) stacking. args A tuple of custom stacking arguments. """ ## LOG ## # This message reports that Zorp is about to stack a new proxy under the current proxy, as a child proxy. ## proxyLog(self, CORE_DEBUG, 7, "Stacking custom child; args='%s'", (str(args))) stack_info = None if isinstance(args[0], str): # this is a Z_STACK_PROVIDER stacking, # args[0] is provider name, # args[1] is stack_info argument stack_backend = getStackingProviderBackend(args[0]) stack_info = args[1] else: # this is a Z_STACK_CUSTOM stacking # args[0] is an AbstractStackingBackend instance # args[1] is optional stack_info stack_backend = args[0] stack_info = args[1] return stack_backend.stack(stack_info) def setServerAddress(self, host, port): """ Function called by the proxy instance to set the address of the destination server. The proxy instance calls this function to set the address of the destination server. This function attempts to resolve the hostname of the server using the DNS; the result is stored in the session.server_address parameter. The address of the server may be modified later by the router of the service. See for details. The setServerAddress function has effect only when InbandRouter is used. host The host name of the server. port The Port number of the server. """ # resolve host, port and store it in session.server_address # may raise an exception if self.session.target_address_inband: target = self.session.service.resolver_policy.resolve(host, port) if not target: ## LOG ## # This message indicates that the given hostname # could not be resolved. It could happen if the # hostname is invalid or nonexistent, or it if your # resolve setting are not well configured. Check # your "/etc/resolv.conf" ## proxyLog(self, CORE_ERROR, 3, "Error resolving hostname; host='%s'", (host,)) return FALSE self.session.setTargetAddress(target) return TRUE def connectServer(self): """ Function called by the proxy instance to establish the server-side connection. This function is called to establish the server-side connection. The function either connects a proxy to the destination server, or an embedded proxy to its parent proxy. The proxy may set the address of the destination server using the setServerAddress function. The connectServer function calls the chainer specified in the service definition to connect to the remote server using the host name and port parameters. The connectServer function returns the descriptor of the server-side data stream. """ try: if self.session.chainer == None: # we have no chainer, the server side fd # should be available by now, used in stacked # proxies if self.session.server_stream == None: raise InternalException, "No chainer and server_stream is None" if self.server_fd_picked: ## LOG ## # This message indicates an internal # error condition, more precisely a # non-toplevel proxy tried to # connect to the server side # multiple times, which is not # supported. Please report this # event to the Zorp QA team (at # devel@balabit.com). ## log(self.session.session_id, CORE_ERROR, 1, "Internal error, stacked proxy reconnected to server multiple times;") return None self.server_fd_picked = TRUE else: self.server_fd_picked = TRUE self.session.server_stream = None self.session.server_local = self.session.owner.server_local self.session.chainer.chainParent(self.session) except ZoneException, s: ## LOG ## # This message indicates that no appropriate zone was found for the server address. # @see: Zone ## log(self.session.session_id, CORE_POLICY, 1, "Zone not found; info='%s'", (s,)) except DACException, s: ## LOG ## # This message indicates that an DAC policy violation occurred. # It is likely that the new connection was not permitted as an inbound_service in the given zone. # @see: Zone ## log(self.session.session_id, CORE_POLICY, 1, "DAC policy violation; info='%s'", (s,)) self.notifyEvent("core.dac_exception", []) except MACException, s: ## LOG ## # This message indicates that a MAC policy violation occurred. ## log(self.session.session_id, CORE_POLICY, 1, "MAC policy violation; info='%s'", (s,)) except AAException, s: ## NOLOG ## log(self.session.session_id, CORE_POLICY, 1, "Authentication failure; info='%s'", (s,)) except LimitException, s: ## NOLOG ## log(self.session.session_id, CORE_POLICY, 1, "Connection over permitted limits; info='%s'", (s,)) except LicenseException, s: ## NOLOG ## log(self.session.session_id, CORE_POLICY, 1, "Attempt to use an unlicensed component, or number of licensed hosts exceeded; info='%s'", (s,)) except: traceback.print_exc() return self.session.server_stream def userAuthenticated(self, entity, groups=None, auth_info=''): """ Function called when inband authentication is successful. The proxy instance calls this function to indicate that the inband authentication was successfully performed. The name of the client is stored in the entity parameter. entity Username of the authenticated client. """ self.session.auth_user = entity self.session.auth_info = auth_info ## LOG ## # This message reports that the user authentication was successful. ## proxyLog(self, CORE_AUTH, 3, "User authentication successful; entity='%s', auth_info='%s'", (entity, auth_info)) update_szig = {'auth_user': entity, 'auth_info': auth_info, 'auth_groups': str(groups),} if auth_info == 'gw-auth': update_szig["gateway_user"] = entity update_szig["gateway_groups"] = str(groups) elif auth_info == 'server': update_szig["remote_user"] = entity update_szig["remote_groups"] = str(groups) szigEvent(Z_SZIG_CONNECTION_PROPS, (Z_SZIG_TYPE_CONNECTION_PROPS, (self.session.service.name, self.session.instance_id, 0, 0, update_szig))) def readPEM(self, filename): """ """ proxyLog(self, CORE_DEBUG, 6, "Reading PEM file; filename='%s'" % filename) f = open(filename, 'r') res = f.read() f.close() return res hash_pattern = re.compile("[0-9a-fA-F]*\.(r){0,1}[0-9]") def readHashDir(self, hash, directory): """ """ try: files = os.listdir(directory) i = 0 for file in files: if self.hash_pattern.match(file): try: hash[i] = self.readPEM(directory + '/' + file) except (TypeError, ValueError), s: proxyLog(self, SSL_ERROR, 3, "Error adding CA certificate; reason='%s'" % (s,)) i = i+1 except OSError, e: proxyLog(self, SSL_ERROR, 3, "Error reading CA or CRL directory; dir='%s', error='%s'", (directory, e.strerror)) def verifyTrustedCert(self, side, verify_results, trusted_certs_dir, blob): """ """ if trusted_certs_dir: if side == 1: f = '%s/%s:%d' % (self.ssl.server_trusted_certs_directory, self.session.server_address.ip_s, self.session.server_address.port) elif side == 0: f = '%s/%s' % (self.ssl.client_trusted_certs_directory, self.session.client_address.ip_s) else: return SSL_HS_ACCEPT proxyLog(self, SSL_DEBUG, 6, "Testing trusted certificates; f='%s'", (f,)) if blob and os.access(f, os.R_OK): if self.readPEM(f) == blob: proxyLog(self, SSL_INFO, 4, "Trusting peer certificate; stored_cert='%s'", f) return SSL_HS_VERIFIED else: proxyLog(self, SSL_VIOLATION, 2, "Peer certificate differs from trusted cert; stored_cert='%s'", f) return SSL_HS_REJECT return SSL_HS_ACCEPT def verifyTrustedCertClient(self, side, verify_results): """ """ res = self.verifyTrustedCert(side, verify_results, self.ssl.client_trusted_certs_directory, self.ssl.client_peer_certificate.blob) if res == SSL_HS_VERIFIED or (res == SSL_HS_ACCEPT and verify_results[0]): self.ssl.client_certificate_trusted = TRUE return res def verifyTrustedCertServer(self, side, verify_results): """ """ res = self.verifyTrustedCert(side, verify_results, self.ssl.server_trusted_certs_directory, self.ssl.server_peer_certificate.blob) if res == SSL_HS_VERIFIED or (res == SSL_HS_ACCEPT and verify_results[0]): self.ssl.server_certificate_trusted = TRUE return res def generateKeyClient(self, side): """ """ # client side, we need to look up the server key if not getattr(self.ssl, "server_peer_certificate", None): proxyLog(self, SSL_ERROR, 4, "Unable to generate certificate for the client, no server certificate present, using configured certificate;") return SSL_HS_ACCEPT if hasattr(self.ssl, "key_generator"): proxyLog(self, SSL_DEBUG, 4, "Generating key for the client; trusted='%d'", self.ssl.server_certificate_trusted) if self.ssl.server_certificate_trusted: (self.ssl.client_local_certificate, self.ssl.client_local_privatekey) = \ self.ssl.key_generator.getKeypair({'bridge-trusted-key': self.ssl.server_peer_certificate.blob}) else: (self.ssl.client_local_certificate, self.ssl.client_local_privatekey) = \ self.ssl.key_generator.getKeypair({'bridge-untrusted-key': self.ssl.server_peer_certificate.blob}) return SSL_HS_ACCEPT else: proxyLog(self, SSL_ERROR, 4, "Unable to generate key for the client, no key generator configured;") return SSL_HS_REJECT def generateKeyServer(self, side): """ """ # server side, we need to look up the client key if not getattr(self.ssl, "client_peer_certificate", None): proxyLog(self, SSL_ERROR, 4, "Unable to generate certificate for the server, no client certificate present, using configured certificate;") return SSL_HS_ACCEPT if hasattr(self.ssl, "key_generator"): proxyLog(self, SSL_DEBUG, 4, "Generating key for the server; trusted='%d'", self.ssl.server_certificate_trusted) if self.ssl.client_certificate_trusted: (self.ssl.server_local_certificate, self.ssl.server_local_privatekey) = \ self.ssl.key_generator.getKeypair({'bridge-trusted-key': self.ssl.client_peer_certificate.blob}) else: (self.ssl.server_local_certificate, self.ssl.server_local_privatekey) = \ self.ssl.key_generator.getKeypair({'bridge-untrusted-key': self.ssl.client_peer_certificate.blob}) return SSL_HS_ACCEPT else: proxyLog(self, SSL_ERROR, 4, "Unable to generate key for the server, no key generator configured;") return SSL_HS_REJECT zorp-3.9.5/pylib/Zorp/Pssl.py000066400000000000000000001740531172670260400161050ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 BalaBit IT Ltd, Budapest, Hungary ## All rights reserved. ## ## ## $Id: Pssl.py,v 1.28 2004/09/23 13:26:46 bazsi Exp $ ## ## Author : Bazsi ## Auditor : ## Last audited version: ## Notes: ## ############################################################################ """ Compatibility proxy for the Secure Socket Layer protocols. This proxy is a compatibility wrapper for core SSL functionality. The documentation of the attributes was left intact for reference, but you should update your configuration to use the common SSL framework available for every proxy. See for details. This documentation describes the now-obsolete Pssl proxy available in the commercial version of Zorp. For the documentation of the SSL proxy available in the open-source Zorp GPL, see the modules/Pssl.py file.
Proxy behavior PsslProxy is a module built for inspecting SSL/TLS connections. SSL/TLS connections initiated from the client are terminated on the firewall; and two separate SSL/TLS connections are built: one between the client and the firewall, and one between the firewall and the server. If both connections are accepted by the local security policy (the certificates are valid, and only the allowed encryption algorithms are used), PsslProxy stacks a proxy to inspect the protocol embedded into the secure channel. The PsslProxy functions as a PlugProxy if no protocol proxy is stacked. Several configuration examples and considerations are discussed in the Technical White Paper and Tutorial Proxying secure channels - the Secure Socket Layer, available at the BalaBit Documentation Page http://www.balabit.com/support/documentation/.
General behavior The proxy starts its operation by inspecting the values set in the handshake_seq attribute. When this attribute is set to PSSL_HSO_CLIENT_SERVER the client side, otherwise (PSSL_HSO_SERVER_CLIENT) the server side handshake is performed first. As part of the handshake process the proxy checks if SSL is enabled on the given side (client_need_ssl and server_need_ssl attributes). It is not necessary for SSL to be enabled on both sides - the proxy can handle one-sided SSL connections as well (e.g.: the firewall communicates in an unencrypted channel with the client, but in a secure channel with the server). If SSL is not enabled, the handshake is skipped for that side. When SSL is needed, the proxy will cooperate with the policy layer to have all required parameters (keys, certificates, etc.) set up. This is achieved using decision points in the hash named handshake_hash which is explained later in detail. The SSL handshake is slightly different for the client (in this case the proxy behaves as an SSL server) and the server (when the proxy behaves as an SSL client).
Client side (SSL server) behavior As an SSL server the first thing to present to an SSL client is a certificate/key pair, thus a call to the 'setup_key' callback is made. It is expected that by the time this callback returns the attributes client_local_privatekey and client_local_certificate are filled appropriately. If peer authentication is enabled (by setting the attribute client_verify_type) a list of trusted CA certificates must be set up (stored in the hash client_local_ca_list). The list can be set up by the 'setup_ca_list' function call. Peer certificates are verified against the trusted CA list and their associated revocation lists. Revocations can be set up in the 'setup_crl_list' callback. At the end of the verification another callback named 'verify_cert' is called which can either ACCEPT or DENY the certificate possibly overriding the verification against the local CA database.
Server side (SSL client) behavior Server side handshake is similar to the client side handshake previously described. The difference is the order of certificate verification. On the server side the proxy verifies the server certificate first and then sends its own certificate for verification. This is unlike the client side where the local certificate is sent first, and then the peer's certificate is verified. So the callbacks are called in this order: 'setup_ca_list' and 'setup_crl_list' to set up CA and CRL information, 'verify_cert' to finalize certificate validation, and 'setup_key' to optionally provide a local certificate/key pair.
Handshake callbacks As described earlier, the proxy provides a way to customize the SSL handshake process. This is done using the client_handshake and server_handshake hashes. These hashes are indexed by the keywords listed below. The tuple can be separated to two parts: 1) tuple type, 2) parameters for the given type. For now only 'PSSL_HS_POLICY' is valid as tuple type, and it requires a function reference as parameter. The following keys are accepted as indexes: setup_key -- This function is called when the proxy needs the private key/certificate pair to be set up. All attributes filled in the earlier phases can be used to decide which key/certificate to use. The function expects two parameters: self, side. setup_ca_list -- This function is called when the proxy needs the trusted CA list to be set up. The function expects two parameters: self, side. setup_crl_list -- This function is called when the proxy needs the CRL list to be set up. This function gets a single string parameter which contains the name of the CA whose CRL is to be filled up. The function expects three parameters: self, side, ca_name. verify_cert -- This function is called to finalize the verification process. The function expects two parameters: self, side. The function arguments as referenced above are defined as: self -- The proxy instance. side -- The side where handshake is being performed. ca_name -- Name of an X.509 certificate. The functions return one of the 'PSSL_HS_*' constants. Generally if the function returns 'PSSL_HS_ACCEPT' the handshake continues, otherwise the handshake is aborted. As an exception, 'verify_cert' may return 'PSSL_HS_VERIFIED' in which case the certificate is accepted without further verification.
X.509 Certificates An X.509 certificate is a public key with a subject name specified as an X.500 DN (distinguished name) signed by a certificate issuing authority (CA). X.509 certificates are represented as Python policy objects having the following attributes: subject -- Subject of the certificate. issuer -- Issuer of the certificate (i.e. the CA that signed it). serial -- Serial number of the certificate. blob -- The certificate itself as a string in PEM format. Zorp uses X.509 certificates to provide a convenient and efficient way to manage and distribute certificates and keys used by the various components and proxies of the managed Zorp hosts. It is mainly aimed at providing certificates required for the secure communication between the different parts of the firewall system, e.g. Zorp hosts and ZMS engine (the actual communication is realized by agents). Certificates of trusted CAs (and their accompanying CRLs) are used in Zorp to validate the certificates of servers accessed by the clients. The hashes and structures below are used by the various certificate-related attributes of the Zorp Pssl proxy, particularly the ones of certificate type.
X.509 Certificate Names A certificate name behaves as a string, and contains a DN in the following format (also known as one-line format): /RDN=value/RDN=value/.../RDN=value/ The word RDN stands for relative distinguished name. For example the DN below: cn=Root CA, ou=CA Group, o=Foo Ltd, l=Bar, st=Foobar State, c=US becomes: /C=US/ST=Foobar State/L=Bar/O=Foo Ltd/OU=CA Group/CN=Root CA/ The format and representation of certificate names may change in future releases.
X.509 Certificate Revocation List A certifying authority may revoke the issued certificates. A revocation means that the serial number and the revocation date is added to the list of revoked certificates. Revocations are published on a regular basis. This list is called the Certificate Revocation List, also known as CRL. A CRL always has an issuer, a date when the list was published, and the expected date of its next update.
X.509 Certificate hash The proxy stores trusted CA certificates in a Certificate hash. This hash can be indexed by two different types. If an integer index is used, the slot specified by this value is looked up; if a string index is used, it is interpreted as a one-line DN value, and the appropriate certificate is looked up. Each slot in this hash contains an X.509 certificate.
X.509 CRL hash Similarly to the certificate hash, a separate hash for storing Certificate Revocation Lists was defined. A CRL contains revocation lists associated to CAs.
Certificate verification options Zorp is able to automatically verify the certificates received. The types of accepted certificates can be controlled separately on the client and the server side using the client_verify_type and the server_verify_type attributes. These attributes offer an easy way to restrict encrypted access only to sites having trustworthy certificates. The available options are summarized in the following table. The server_check_subject can be used to compare the domain name provided in the Subject field of the server certificate to application level information about the server. Currently it can compare the Subject field to the domain name of the HTTP request in HTTPS communication. If the server_check_subject is set to TRUE and server_verify_type is SSL_VERIFY_REQUIRED_UNTRUSTED or SSL_VERIFY_REQUIRED_TRUSTED, the HTTP proxy stacked into SSL will deny access to the page and return an error if the Subject field does not match the domain name of the URL.
Setting the allowed SSL/TLS protocol As there are different and sometimes incompatible releases of the SSL protocol, it is possible to specify which SSL/TLS version is allowed to pass the firewall. The attributes client_ssl_method and server_ssl_method can be used for this purpose. Specify the appropriate 'PSSL_METHOD_*' constant to allow the selected protocol. Only one constant can be specified. Zorp currently supports the SSL versions 2 and 3 and the TLS v1 protocols. The OpenSSL implementation of the SSL protocol (used by Zorp) has an important feature regarding method selection: it allows automatical fallbacks if one side does not support the selected method. That means that even if PSSL_METHOD_SSLv3 is specified, the communication might fall back to SSLv2 if one f the communicating parties does not support v3. To explicitly deny a protocol, set the appropriate client_disable_proto_* or server_disable_proto_* attribute to TRUE. In Zorp SSLv2 is disabled by default.
SSL cipher selection The cipher algorithms used for key exchange and mass symmetric encryption are specified by the attributes client_ssl_ciphers and server_ssl_ciphers. These attributes contain a cipher specification as specified by the OpenSSL manuals, see the manual page ciphers(ssl) for further details. The default set of ciphers can be set by using the following predefined variables. Cipher specifications as defined above are sorted by key length, the cipher providing the best key length will be the most preferred.
Related standards The SSL protocol is defined by Netscape Ltd. at http://wp.netscape.com/eng/ssl3/ssl-toc.html The TLS protocol is defined in RFC 2246.
SSL protocol verification hashes SSL_VERIFY_NONE SSL_VERIFY_OPTIONAL SSL_VERIFY_REQUIRED_UNTRUSTED SSL_VERIFY_REQUIRED_TRUSTED Certificate verification settings PSSL_VERIFY_NONE Automatic certificate verification is disabled. PSSL_VERIFY_OPTIONAL_UNTRUSTED Certificate is optional, if present, both trusted and untrusted certificates are accepted. PSSL_VERIFY_OPTIONAL_TRUSTED Certificate is optional, but if a certificate is present, only certificates signed by a trusted CA are accepted. PSSL_VERIFY_REQUIRED_UNTRUSTED Valid certificate is required, both trusted and untrusted certificates are accepted. PSSL_VERIFY_REQUIRED_TRUSTED Certificate is required, only valid certificates signed by a trusted CA are accepted. Constants for SSL/TLS protocol selection PSSL_METHOD_SSLV23 Permit the use of SSLv2 and v3. PSSL_METHOD_SSLV2 Permit the use of SSLv2 exclusively. PSSL_METHOD_SSLV3 Permit the use of SSLv3 exclusively. PSSL_METHOD_TLSV1 Permit the use of TLSv1 exclusively. PSSL_METHOD_ALL Permit the use of all the supported (SSLv2, SSLv3, and TLSv1) protocols. Constants for cipher selection PSSL_CIPHERS_ALL Permit the use of all supported ciphers, including the 40 and 56 bit exportable ciphers. PSSL_CIPHERS_HIGH Permit only the use of ciphers which use at least 128 bit long keys. PSSL_CIPHERS_MEDIUM Permit only the use of ciphers which use 128 bit long keys. PSSL_CIPHERS_LOW Permit only the use of ciphers which use keys shorter then 128 bits. handshake order PSSL_HSO_CLIENT_SERVER PSSL_HSO_SERVER_CLIENT pssl proxy printed log messages PSSL_ERROR 'pssl.error' PSSL_DEBUG 'pssl.debug' handshake policy decisions PSSL_HS_ACCEPT 0 PSSL_HS_REJECT 1 PSSL_HS_POLICY 6 PSSL_HS_VERIFIED 10
""" from Zorp import * from Proxy import proxyLog from Proxy import SSL_VERIFY_NONE, SSL_VERIFY_OPTIONAL, \ SSL_VERIFY_OPTIONAL_UNTRUSTED, SSL_VERIFY_OPTIONAL_TRUSTED, \ SSL_VERIFY_REQUIRED_UNTRUSTED, SSL_VERIFY_REQUIRED_TRUSTED, \ SSL_HSO_CLIENT_SERVER, SSL_HSO_SERVER_CLIENT, SSL_HS_ACCEPT, \ SSL_HS_REJECT, SSL_HS_POLICY, SSL_HS_VERIFIED, SSL_METHOD_SSLV23, \ SSL_METHOD_SSLV2, SSL_METHOD_SSLV3, SSL_METHOD_TLSV1, SSL_METHOD_ALL, \ SSL_CIPHERS_ALL, SSL_CIPHERS_HIGH, SSL_CIPHERS_MEDIUM, SSL_CIPHERS_LOW, \ SSL_NONE, SSL_FORCE_SSL, SSL_ACCEPT_STARTTLS, SSL_FORWARD_STARTTLS from Plug import * import re import os import types import thread import string import fcntl PSSL_ERROR = 'pssl.error' PSSL_DEBUG = 'pssl.debug' PSSL_INFO = 'pssl.info' PSSL_VIOLATION = 'pssl.violation' PSSL_VERIFY_NONE = SSL_VERIFY_NONE PSSL_VERIFY_OPTIONAL = SSL_VERIFY_OPTIONAL PSSL_VERIFY_OPTIONAL_UNTRUSTED = SSL_VERIFY_OPTIONAL_UNTRUSTED PSSL_VERIFY_OPTIONAL_TRUSTED = SSL_VERIFY_OPTIONAL_TRUSTED PSSL_VERIFY_REQUIRED_UNTRUSTED = SSL_VERIFY_REQUIRED_UNTRUSTED PSSL_VERIFY_REQUIRED_TRUSTED = SSL_VERIFY_REQUIRED_TRUSTED PSSL_HSO_CLIENT_SERVER = SSL_HSO_CLIENT_SERVER PSSL_HSO_SERVER_CLIENT = SSL_HSO_SERVER_CLIENT PSSL_HS_ACCEPT = SSL_HS_ACCEPT PSSL_HS_REJECT = SSL_HS_REJECT PSSL_HS_POLICY = SSL_HS_POLICY PSSL_HS_VERIFIED = SSL_HS_VERIFIED PSSL_METHOD_SSLV23 = SSL_METHOD_SSLV23 PSSL_METHOD_SSLV2 = SSL_METHOD_SSLV2 PSSL_METHOD_SSLV3 = SSL_METHOD_SSLV3 PSSL_METHOD_TLSV1 = SSL_METHOD_TLSV1 PSSL_METHOD_ALL = SSL_METHOD_ALL PSSL_CIPHERS_ALL = SSL_CIPHERS_ALL PSSL_CIPHERS_HIGH = SSL_CIPHERS_HIGH PSSL_CIPHERS_MEDIUM = SSL_CIPHERS_MEDIUM PSSL_CIPHERS_LOW = SSL_CIPHERS_LOW PSSL_NONE = SSL_NONE PSSL_FORCE_SSL = SSL_FORCE_SSL PSSL_ACCEPT_STARTTLS = SSL_ACCEPT_STARTTLS PSSL_FORWARD_STARTTLS = SSL_FORWARD_STARTTLS class AbstractPsslProxy(AbstractPlugProxy): """ Class encapsulating the abstract Pssl proxy. This proxy is a compatibility wrapper for core SSL functionality. The documentation was left intact for reference, but SSL attributes should be used in each protocol proxy instead of this wrapper. This abstract class encapsulates a plug proxy which uses SSL/TLS on both or either sides, making it possible for any protocol proxy to analyze protocol streams embedded into SSL/TLS. AbstractPsslProxy serves as a starting point for customized proxy classes and is itself not directly usable. Service definitions should refer to a customized class derived from AbstractPsslProxy, or the predefined PsslProxy class. copy_to_server TRUE Allow data transfer in the client->server direction. copy_to_client TRUE Allow data transfer in the server->client direction. bandwidth_to_client n/a Read-only variable containing the bandwidth currently used in server->client direction. bandwidth_to_server n/a Read-only variable containing the bandwidth currently used in client->server direction. packet_stats_interval 0 The number of passing packages between two successive packetStats() events. Set to 0 to turn packetStats() off. stack_proxy The proxy class to stack into the connection to inspect the embedded protocol. See also . timeout 600000 I/O timeout in milliseconds. handshake_timeout 30000 SSL handshake timeout in milliseconds. permit_invalid_certificates FALSE Accept any kind of verification failure when UNTRUSTED verify_type is set. E.g.: accept expired, self-signed, etc. certificates. handshake_seq PSSL_HSO_CLIENT_SERVER Handshake order. PSSL_HSO_CLIENT_SERVER performs the client side handshake first, PSSL_HSO_SERVER_CLIENT the server side. client_need_ssl TRUE Enable SSL on the client side of the proxy. This requires setting up a client private key and a certificate. client_handshake HASH:empty:RW:R empty Specifies policy callbacks for various SSL handshake phases. client_verify_type PSSL_VERIFY_REQUIRED_TRUSTED Verification setting of the peer certificate on the client side. client_verify_depth 4 The longest accepted CA verification chain. client_local_privatekey empty The private key of the firewall on the client side. Specified as a string in PEM format. client_local_privatekey_passphrase n/a Passphrase used to access client_local_privatekey. client_local_certificate X509:empty:RW:RW empty The certificate associated to client_local_privatekey to be used on the client side. client_peer_certificate X509:empty:R:R empty The certificate returned by the peer on the client side. client_local_ca_list HASH;INTEGER;X509:empty:RW:RW empty A hash of trusted certificates. The items in this hash are used to verify client certificates. client_local_crl_list HASH;INTEGER;X509_CRL:empty:RW:RW empty A hash of Certificate Revocation Lists, associated to CA certificates in client_local_ca_list. client_ssl_method PSSL_METHOD_ALL Specifies the allowed SSL/TLS protocols on the client side. client_disable_proto_sslv2 TRUE Specifies that SSLv2 should be disabled even if the method selection would otherwise support SSLv2. client_disable_proto_sslv3 FALSE Specifies that SSLv3 should be disabled even if the method selection would otherwise support SSLv3. client_disable_proto_tlsv1 FALSE Specifies that TLSv1 should be disabled even if the method selection would otherwise support TLSv1. client_ssl_cipher PSSL_CIPHERS_ALL Specifies the allowed ciphers on the client side. server_need_ssl TRUE Enable SSL on the server side of the proxy. This requires setting up a private key and a certificate on Zorp. server_handshake HASH:empty:RW:R empty Specifies policy callbacks for various SSL handshake phases. server_verify_type PSSL_VERIFY_REQUIRED_TRUSTED Verification settings of the peer certificate on the server side. server_verify_depth 4 The longest accepted CA verification chain. server_local_privatekey empty The private key of the firewall on the server side, specified as a string in PEM format. Server side key and certificate are optional. server_local_privatekey_passphrase n/a Passphrase used to access server_local_privatekey. server_local_certificate X509:empty:RW:RW empty The certificate to be used on the server side, associated with server_local_privatekey. server_peer_certificate X509:empty:R:R empty The certificate returned by the peer on the server side. server_local_ca_list HASH;INTEGER;X509:empty:RW:RW empty Hash of trusted certificates. The items in this hash are used to verify server certificates. server_peer_ca_list HASH;INTEGER;X509:empty:RW:RW empty Hash of names of trusted CAs as returned by the server to aid the selection of a local certificate. server_local_crl_list HASH;INTEGER;X509_CRL:empty:RW:RW empty Hash of Certificate Revocation Lists, associated to CA certificates in server_local_ca_list. server_ssl_method PSSL_METHOD_ALL Specifies the SSL/TLS protocols allowed on the server side. server_disable_proto_sslv2 TRUE Specifies that SSLv2 should be disabled even if the method selection would otherwise support SSLv2. server_disable_proto_sslv3 FALSE Specifies that SSLv3 should be disabled even if the method selection would otherwise support SSLv3. server_disable_proto_tlsv1 FALSE Specifies that TLSv1 should be disabled even if the method selection would otherwise support TLSv1. server_ssl_cipher PSSL_CIPHERS_ALL Specifies the ciphers allowed on the server side. server_check_subject TRUE Specifies whether the Subject of the server side certificate is checked against application layer information (e.g.: whether it matches the hostname in the URL). See also . """ name = "plug" compat_attributes = frozenset(( \ 'handshake_timeout', \ 'permit_invalid_certificates', \ 'handshake_seq', \ 'client_handshake', \ 'client_verify_type', \ 'client_verify_depth', \ 'client_local_privatekey', \ 'client_local_privatekey_passphrase', \ 'client_local_certificate', \ 'client_peer_certificate', \ 'client_local_ca_list', \ 'client_local_crl_list', \ 'client_ssl_method', \ 'client_disable_proto_sslv2', \ 'client_disable_proto_sslv3', \ 'client_disable_proto_tlsv1', \ 'client_ssl_cipher', \ 'server_handshake', \ 'server_verify_type', \ 'server_verify_depth', \ 'server_local_privatekey', \ 'server_local_privatekey_passphrase', \ 'server_local_certificate', \ 'server_peer_certificate', \ 'server_local_ca_list', \ 'server_peer_ca_list', \ 'server_local_crl_list', \ 'server_ssl_method', \ 'server_disable_proto_sslv2', \ 'server_disable_proto_sslv3', \ 'server_disable_proto_tlsv1', \ 'server_ssl_cipher', \ 'server_check_subject', \ 'client_cert_file', \ 'client_key_file', \ 'client_keypair_files', \ 'client_keypair_generate', \ 'client_ca_directory', \ 'client_crl_directory', \ 'client_cagroup_directories', \ 'client_trusted_certs_directory', \ 'server_cert_file', \ 'server_key_file', \ 'server_keypair_files', \ 'server_keypair_generate', \ 'server_ca_directory', \ 'server_crl_directory', \ 'server_cagroup_directories', \ 'server_trusted_certs_directory', \ 'key_generator', \ 'client_cert', \ 'client_key', \ 'server_cert', \ 'server_key' )) compat_value_ssl_enabled = { \ FALSE: PSSL_NONE, \ TRUE: PSSL_FORCE_SSL } compat_value_ssl_enabled_rev = { \ PSSL_NONE: FALSE, \ PSSL_FORCE_SSL: TRUE } def __init__(self, session): """ Constructor to initialize a PsslProxy instance. Sets attributes based on arguments, and calls the inherited constructor. session Session the reference of the owning session type type of this session, must indicate STREAM session, since SSL is supported only on TCP streams """ self.stack_proxy = None super(AbstractPsslProxy, self).__init__(session) def __pre_config__(self): """ """ super(AbstractPsslProxy, self).__pre_config__() self.ssl.key_generator = self.key_generator self.ssl.client_connection_security = PSSL_FORCE_SSL self.ssl.server_connection_security = PSSL_FORCE_SSL def requestStack(self): """ Function used to query whether to stack anything to Pssl. Callback called by the underlying C proxy to query if something is to be stacked. """ return self.stack_proxy def __setattr__(self, name, value): if name == "client_need_ssl": setattr(self.ssl, "client_connection_security", self.compat_value_ssl_enabled[value]) elif name == "server_need_ssl": setattr(self.ssl, "server_connection_security", self.compat_value_ssl_enabled[value]) elif name in self.compat_attributes: setattr(self.ssl, name, value) else: super(AbstractPsslProxy, self).__setattr__(name, value) def __getattr__(self, name): if name == "client_need_ssl": value = getattr(self.ssl, "client_connection_security") value = self.compat_value_ssl_enabled_rev[value] elif name == "server_need_ssl": value = getattr(self.ssl, "server_connection_security") value = self.compat_value_ssl_enabled_rev[value] elif name in self.compat_attributes: value = getattr(self.ssl, name) else: value = super(AbstractPsslProxy, self).getattr(name) return value class PsslProxy(AbstractPsslProxy): """ Default Pssl proxy based on AbstractPsslProxy. This proxy is a compatibility wrapper for core SSL functionality. The documentation was left intact for reference, but SSL attributes should be used in each protocol proxy instead of this wrapper. PsslProxy is a default class for proxying SSL/TLS connections based on AbstractPsslProxy. client_cert_file "" File containing the client side certificate. client_key_file "" File containing the client side private key. client_keypair_files "" A tuple of two file names containing the certificate and key files. Using client_keypair_files is alternative to using the client_cert_file and client_key_file attributes. client_keypair_generate FALSE Enables keybridging towards the clients. (Specifies whether to generate new certificates.) client_ca_directory "" Directory where the trusted CA certificates are stored. client_crl_directory "" Directory where the CRLs associated with trusted CAs are stored. client_cagroup_directories "" A tuple of the trusted CA certificate directory and the corresponding CRL directory. client_trusted_certs_directory "" A directory where trusted IP - certificate assignments are stored. When a specific IP address introduces itself with the certificate stored in this directory, it is accepted regardless of its expiration or issuer CA. Each file in the directory should contain a certificate in PEM format and have the name of the IP address. server_cert_file "" File containing the server side certificate. server_key_file "" File containing the server side private key. server_keypair_files "" A tuple of two file names containing the certificate and key files. Using client_keypair_files is alternative to use the client_cert_file and client_key_file attributes. server_keypair_generate FALSE Enables keybridging towards the server. (Specifies whether to generate new certificates.) server_ca_directory "" Directory where the trusted CA certificates are stored. server_crl_directory "" Directory where the CRLs associated with the trusted CAs are stored. server_cagroup_directories "" A tuple of the trusted CA certificate directory and the corresponding CRL directory. server_trusted_certs_directory "" A directory where trusted IP:port - certificate assignments are stored. When a specific IP address introduces itself with the certificate stored in this directory, it is accepted regardless of its expiration or issuer CA. Each file in the directory should contain a certificate in PEM format and should be named as 'IP:PORT'. key_generator An instance of a X509KeyManager or derived class to generate keys automatically based on the keys on one of the other peers. Use X509KeyBridge to generate certificates automatically with a firewall hosted local CA. client_cert Alias for Zorp 0.8 compatibility same as client_cert_file client_key Alias for Zorp 0.8 compatibility same as client_key_file server_cert Alias for Zorp 0.8 compatibility same as server_cert_file server_key Alias for Zorp 0.8 compatibility same as server_key_file """ key_generator = None zorp-3.9.5/pylib/Zorp/Receiver.py000066400000000000000000000357011172670260400167240ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Receiver module defines the classes that accept incoming UDP connections. Receivers are identical to Listeners, but they accept UDP connections. """ from Dispatch import * class Receiver(Dispatcher): """ Class encapsulating the default Receiver. Receivers listen for incoming UDP connections on a port and start a session and a service for accepted connections. """ def __init__(self, bindto, service, **kw): """ Constructor to initialize an instance of the Receiver class. This constructor creates a new Receiver instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Receiver accepts connections. service Name of the service to start. transparent Set this parameter to TRUE if the receiver starts a transparent service. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the listener will start from the listener's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. mark_tproxy Set this parameter to TRUE to mark all connections accepted by the Listener with the -m tproxy IPtables label. session_limit Specifies the maximum number of proxies permitted to start in a single poll loop. """ super(Receiver, self).__init__(convertSockAddrToDB(bindto, ZD_PROTO_UDP), service, **kw) class ZoneReceiver(ZoneDispatcher): """ Class encapsulating a Receiver which selects a service based on the client zone. See for details. """ def __init__(self, bindto, services, **kw): """ Constructor to initialize an instance of the ZoneReceiver class. This constructor creates a new ZoneReceiver instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Receiver accepts connections. services Client zone - service name pairs using the ("zone":"service") format; specifying the service to start when the receiver accepts a connection from the given client zone. transparent Set this parameter to TRUE if the receiver starts a transparent service. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the listener will start from the listener's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. mark_tproxy Set this parameter to TRUE to mark all connections accepted by the Listener with the -m tproxy IPtables label. session_limit Specifies the maximum number of proxies permitted to start in a single poll loop. """ super(ZoneReceiver, self).__init__(convertSockAddrToDB(bindto, ZD_PROTO_UDP), services, **kw) class CSZoneReceiver(CSZoneDispatcher): """ Class encapsulating a Receiver which selects a service based on the client and the server zone. See for details. CSZoneReceivers are similar to Receivers, but select a service based on the zone of the client and the destination server. See for details. The server zone may be modified by the proxy, the router, the chainer, or the NAT policy used in the service. To select the service, CSZoneListener determines the server zone from the original destination IP address of the incoming client request. Similarly, the client zone is determined from the source IP address of the original client request. """ def __init__(self, bindto, services, **kw): """ Constructor to initialize a CSZoneReceiver instance. This constructor creates a new CSZoneListener instance which can be associated with a Service. bindto An existing socket address containing the IP address and port number where the Receiver accepts connections. service Client zone - server zone - service name pairs using the (("client_zone","server_zone"):"service") format; specifying the service to start when the receiver accepts a connection from the given client zone. transparent Set this parameter to TRUE if the receiver starts a transparent service. threaded Set this parameter to TRUE to start a new thread for every client request. The proxy threads started by the listener will start from the listener's thread instead of the main Zorp thread. Zorp accepts incoming connections faster and optimizes queuing if this option is enabled. This improves user experience, but significantly increases the memory consumption of Zorp. Use it only if Zorp has to transfer a very high number of concurrent connections. mark_tproxy Set this parameter to TRUE to mark all connections accepted by the Listener with the -m tproxy IPtables label. session_limit Integer Specifies the maximum number of proxies permitted to start in a single poll loop. """ super(CSZoneReceiver, self).__init__(convertSockAddrToDB(bindto, ZD_PROTO_UDP), services, **kw) zorp-3.9.5/pylib/Zorp/Resolver.py000066400000000000000000000243361172670260400167630ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining the resolver used to resolve domain names to IP addresses. This module defines the AbstractResolver interface and various derived classes to perform name lookups. """ from Zorp import * from SockAddr import SockAddrInet, SockAddrInet6 import socket import types def create_sockaddr(addrinfo, port): addr = addrinfo[4][0] family = addrinfo[0] if (family == socket.AF_INET): return SockAddrInet(addr, port) elif (family == socket.AF_INET6): return SockAddrInet6(addr, port) class ResolverPolicy(object): """ Class encapsulating a Resolver which can be referenced using its identifier Resolvers and resolver policies specify how a Zorp service should resolve the domain names in client requests; resolvers are used whenever Zorp needs to resolve domain names in order to perform connection processing. Such an event occurs when InbandRouter is used and the Zorp proxy has a DNS name to establish connection to. Names are usually resolved using the domain name server ( DNSResolver class), or the HashResolver class when the dependence on DNS has to be avoided. To actually perform name resolution, you have to use a ResolverPolicy instance that contains a configured Resolver class. Resolver policies provide a way to re-use Resolver instances whithout having to define a Resolver for each service individually. """ def __init__(self, name, resolver): """ name Name identifying the Resolver policy. resolver Resolver object which performs name resolution. """ self.name = name self.resolver = resolver if name: if Globals.resolver_policies.has_key(name): raise ValueError, "Duplicate ResolverPolicy: %s" % name Globals.resolver_policies[name] = self def resolve(self, host, port): """ """ return self.resolver.resolve(host, port) def getResolverPolicy(name): """ """ if name: if Globals.resolver_policies.has_key(name): return Globals.resolver_policies[name] else: log(None, CORE_POLICY, 3, "No such resolver policy; policy='%s'", name) return None class AbstractResolver(object): """ Class encapsulating the abstract Resolver interface. This class encapsulates an interface for application level name resolution. """ def __init__(self): """ """ pass def resolve(self, host, port): """ """ raise NotImplementedError class DNSResolver(AbstractResolver): """ Class encapsulating DNS-based name resolution. DNSResolver policies query the domain name server used by Zorp in general to resolve domain names. A simple DNSResolver policy Below is a simple DNSResolver policy enabled to return multiple 'A' records. ResolverPolicy(name="Mailservers", resolver=DNSResolver(multi=TRUE)) """ def __init__(self, multi=FALSE, family=AF_UNSPEC): """ Constructor to initialize a DNSResolver instance. This constructor initializes a DNSResolver instance. multi FALSE Enable this attribute to retrieve multiple IP addresses from the DNS server if the domain name has multiple A records. family AF_UNSPEC Set this attribute to the necessary address family to filter retrieved IP addresses from the DNS server when name has multiple A records. """ super(DNSResolver, self).__init__() self.multi = multi self.family = family def resolve(self, host, port): """ """ try: addrinfos = socket.getaddrinfo(host, None, self.family) if self.multi: return map(lambda addrinfo: create_sockaddr(addrinfo, port), addrinfos) else: return (create_sockaddr(addrinfos[0], port)) except IOError: return None class HashResolver(AbstractResolver): """ Class encapsulating hash-based name resolution. HashResolver policies are used to locally store the IP addresses belonging to a domain name. A domain name (Hostname) and one or more corresponding IP addresses (Addresses) can be stored in a hash. If the domain name to be resolved is not included in the hash, the name resolution will fail. The HashResolver can be used to direct incoming connections to specific servers based on the target domain name. A simple HashResolver policy The resolver policy below associates the IP addresses 192.168.1.12 and 192.168.1.13 with the mail.example.com domain name. ResolverPolicy(name="DMZ", \ resolver=HashResolver(mapping={"mail.example.com":\ ("192.168.1.12", "192.168.1.13")})) """ def __init__(self, mapping): """ Constructor to initialize a HashResolver instance. This constructor initializes a HashResolver instance. mapping Mapping that describes hostname->IP address pairs. """ super(HashResolver, self).__init__() self.mapping = mapping def resolve(self, host, port): """ """ try: ip_list = self.mapping[host] if type(ip_list) == types.StringType: ip_list = (ip_list,) return map(lambda ip: create_sockaddr(socket.getaddrinfo(ip, None)[0], port), ip_list) except KeyError: return None zorp-3.9.5/pylib/Zorp/Router.py000066400000000000000000000630601172670260400164370ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Router module defines the classes that select the destination address of the server-side connection. Routers define the target IP address and port of the destination server, based on information that is available before the Zorp proxy is started. The simplest router (DirectedRouter) selects a preset destination as the server address, while the most commonly used TransparentRouter connects to the IP address requested by the client. Other routers may make more complex decisions. The destination address selected by the router may be overridden by the proxy and the DNAT classes used in the service.
The source address used in the server-side connection Routers also define source address and port of the server-side connection. This is the IP address that Zorp uses to connect the server. The server sees that the connection originates from this address. The following two parameters determine the source address used in the server-side connection: forge_addr: If set to TRUE, Zorp uses the client's source address as the source of the server-side connection. Otherwise, Zorp uses the IP address of the interface connected to the server. forge_port: This parameter defines the source port that Zorp uses in the server-side connection. Specify a port number as an integer value, or use one of the following options: Options defining the source port of the server-side connection NameDescription Z_PORT_ANY Selected a random port between 1024 and 65535. This is the default behavior of every router. Z_PORT_GROUP Select a random port in the same group as the port used by the client. The following groups are defined: 0-513, 514-1024, 1025-. Z_PORT_EXACT Use the same port as the client. Z_PORT_RANDOM Select a random port using a cryptographically secure function.
""" from Zorp import * from SockAddr import SockAddrInet class AbstractRouter(object): """ Class encapsulating the abstract router. AbstractRouter implements an abstract router that determines the destination address of the server-side connection. Service definitions should refer to a customized class derived from AbstractRouter, or one of the predefined router classes, such as TransparentRouter or DirectedRouter. Different implementations of this interface perform Transparent routing (directing the client to its original destination), and Directed routing (directing the client to a given destination). A proxy can override the destination selected by the router using the the setServerAddress method. forge_addr If set to TRUE, Zorp uses the client's source address as the source of the server-side connection. forge_port Defines the source port that Zorp uses in the server-side connection. See for details. """ def __init__(self, forge_addr, forge_port): """ forge_addr FALSE set to true if the client's source address is to be forged on the server side forge_port Z_PORT_ANY Defines the source port that Zorp uses in the server-side connection. See for details. """ self.forge_addr = forge_addr self.forge_port = forge_port def routeConnection(self, session): """ Function called to perform connection routing. This function is called to determine the destination address of this session, and place it in session.target_address session session we belong to """ session.target_local_loose = FALSE session.target_local_random = FALSE # defaults to FALSE, but just in case if self.forge_port == Z_PORT_ANY: local_port = 0 elif self.forge_port == Z_PORT_GROUP: local_port = session.client_address.port session.target_local_loose = TRUE elif self.forge_port == Z_PORT_EXACT: local_port = session.client_address.port elif self.forge_port == Z_PORT_RANDOM: local_port = session.client_address.port session.target_local_loose = TRUE session.target_local_random = TRUE elif self.forge_port >= 1 and self.forge_port <= 65535: local_port = self.forge_port else: raise ValueError, "Invalid forge_port value (%d)" % self.forge_port if self.forge_addr or session.service.snat_policy: local_addr = session.client_address.clone(FALSE) local_addr.port = local_port else: if local_port != 0: local_addr = session.client_address.clone(FALSE) local_addr.ip = 0 local_addr_port = local_port else: local_addr = None if local_addr: session.target_local = local_addr class TransparentRouter(AbstractRouter): """ Class encapsulating a Router which provides transparent services. This class implements transparent routing, which means that the destination server is the original destination requested by the client. TransparentRouter example The following service uses a TransparentRouter that connects to the 8080 port of the server and uses the client's IP address as the source of the server-side connection. Service(name="demo_service", proxy_class=HttpProxy, router=TransparentRouter(forced_port=8080, overrideable=FALSE, forge_addr=TRUE)) forced_port Defines the source port that Zorp uses in the server-side connection. See for details. forge_addr If set to TRUE, Zorp uses the client's source address as the source of the server-side connection. """ def __init__(self, forced_port=0, forge_addr=FALSE, overrideable=FALSE, forge_port=Z_PORT_ANY): """ Constructor to initialize an instance of the TransparentRouter class. This constructor creates a new TransparentRouter instance which can be associated with a Service. forced_port 0 Modify the destination port to this value. Default value: 0 (do not modify the target port) forge_addr FALSE If set to TRUE, Zorp uses the client's source address as the source of the server-side connection. forge_port Z_PORT_ANY Defines the source port that Zorp uses in the server-side connection. See for details. overrideable FALSE If set to TRUE, the proxy may override the selected destination. Enable this option when the proxy builds multiple connections to the destination server, and the proxy knows the address of the destination server, for example, because it receives a redirect request. This situation is typical for the SQLNet proxy. """ super(TransparentRouter, self).__init__(forge_addr, forge_port) self.forced_port = forced_port self.overrideable = overrideable def routeConnection(self, session): """ Overridden function to perform routing. This function sets 'session.target_address' to the transparent destination as stored in 'session.client_local'. session session we belong to """ super(TransparentRouter, self).routeConnection(session) addr = session.client_local.clone(FALSE) if self.forced_port: addr.port = self.forced_port session.target_address_inband = self.overrideable session.setTargetAddress((addr,)) class DirectedRouter(AbstractRouter): """ Class encapsulating a Router which explicitly defines the target address. This class implements directed routing, which means that the destination address is a preset address for each session. DirectedRouter example The following service uses a DirectedRouter that redirects every connection to the /var/sample.socket Unix domain socket. Service(name="demo_service", proxy_class=HttpProxy, router=DirectedRouter(dest_addr=SockAddrUnix('/var/sample.socket'), overrideable=FALSE, forge_addr=FALSE)) The following service uses a DirectedRouter that redirects every connection to the 192.168.2.24:8080 IP address. Service(name="demo_service", proxy_class=HttpProxy, router=DirectedRouter(dest_addr=SockAddrInet('192.168.2.24', 8080), overrideable=FALSE, forge_addr=FALSE)) dest_addr The destination address to connect to. """ def __init__(self, dest_addr, forge_addr=FALSE, overrideable=FALSE, forge_port=Z_PORT_ANY): """ Constructor to initialize a DirectedRouter. This constructor initializes an instance of the DirectedRouter class. dest_addr The destination address to connect to. forge_addr FALSE If set to TRUE, Zorp uses the client's source address as the source of the server-side connection. forge_port Z_PORT_ANY Defines the source port that Zorp uses in the server-side connection. See for details. overrideable FALSE If set to TRUE, the proxy may override the selected destination. Enable this option when the proxy builds multiple connections to the destination server, and the proxy knows the address of the destination server, for example, because it receives a redirect request. This situation is typical for the SQLNet proxy. """ super(DirectedRouter, self).__init__(forge_addr, forge_port) if isinstance(dest_addr, SockAddrType): self.dest_addr = (dest_addr,) else: self.dest_addr = dest_addr self.overrideable = overrideable def routeConnection(self, session): """ Overridden function to perform routing. This function simply sets 'session.target_address' to 'self.dest_addr' session session we belong to """ super(DirectedRouter, self).routeConnection(session) session.setTargetAddress(self.dest_addr) session.target_address_inband = self.overrideable class InbandRouter(AbstractRouter): """ Class encapsulating the Router which extracts the destination address from the application-level protocol. This class implements inband routing, which means that the destination address will be determined by the protocol. Inband routing works only for protocols that can send routing information within the protocol, and is mainly used for non-transparent proxying. The InbandRouter class currently supports only the HTTP and FTP protocols. InbandRouter example The following service uses an InbandRouter to extract the destination from the protocol. Service(name="demo_service", proxy_class=HttpProxy, router=InbandRouter(forge_addr=FALSE)) """ def __init__(self, forge_addr=FALSE, forge_port=Z_PORT_ANY): """ Constructor to initialize a InbandRouter. This constructor initializes an instance of the InbandRouter class. forge_addr FALSE If set to TRUE, Zorp uses the client's source address as the source of the server-side connection. forge_port Z_PORT_ANY Defines the source port that Zorp uses in the server-side connection. See for details. """ super(InbandRouter, self).__init__(forge_addr, forge_port) def routeConnection(self, session): """ Overridden function to perform routing. This function does nothing, it simply lets the protocol logic to choose its destination server. session session we belong to """ super(InbandRouter, self).routeConnection(session) session.target_address_inband = TRUE zorp-3.9.5/pylib/Zorp/Rule.py000066400000000000000000000222771172670260400160730ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ """ from Util import makeSequence from Subnet import Subnet from Zone import Zone import kznf.kznfnetlink as kznf import Globals import Dispatch class RuleSet(object): def __init__(self): self._rules = [] self._rule_id_index = 1 self._rule_id_set = set() def _getNextId(self): while (self._rule_id_index in self._rule_id_set): self._rule_id_index += 1 return self._rule_id_index def add(self, rule): rule_id = rule.getId() if not rule_id: # generate a unique id rule_id = self._getNextId() rule.setId(rule_id) elif rule_id in self._rule_id_set: # the specified id is not unique raise ValueError, "Duplicate rule id found; id='%d'" % (rule_id,) self._rule_id_set.add(rule_id) self._rules.append(rule) def _sortRules(self): self._rules.sort(lambda a, b: cmp(a.getId(), b.getId())) def __iter__(self): self._sortRules() return iter(self._rules) @property def length(self): return len(self._rules) class PortRange(object): def __init__(self, low, high): self._low = low self._high = high def getTuple(self): return (self._low, self._high) class Rule(object): valid_dimensions = { 'iface' : kznf.KZA_N_DIMENSION_IFACE, \ 'ifgroup' : kznf.KZA_N_DIMENSION_IFGROUP, \ 'proto' : kznf.KZA_N_DIMENSION_PROTO, \ 'src_port' : kznf.KZA_N_DIMENSION_SRC_PORT, \ 'dst_port' : kznf.KZA_N_DIMENSION_DST_PORT, \ 'src_subnet' : kznf.KZA_N_DIMENSION_SRC_IP, \ 'src_subnet6' : kznf.KZA_N_DIMENSION_SRC_IP6, \ 'src_zone' : kznf.KZA_N_DIMENSION_SRC_ZONE, \ 'dst_subnet' : kznf.KZA_N_DIMENSION_DST_IP, \ 'dst_subnet6' : kznf.KZA_N_DIMENSION_DST_IP6, \ 'dst_zone' : kznf.KZA_N_DIMENSION_DST_ZONE } def __init__(self, **kw): def parseSubnets(subnet_list): """ Helper function to convert a string-based subnet list to two tuples consisting of InetSubnet and InetSubnet6 instances. """ import socket subnets = { socket.AF_INET: [], socket.AF_INET6: [] } subnet_list = makeSequence(subnet_list) for item in subnet_list: if isinstance(item, basestring): subnet = Subnet.create(item) elif isinstance(item, Subnet): subnet = item else: raise ValueError, "Invalid subnet specification: value='%s'" % (item,) subnets[subnet.get_family()].append((subnet.addr_packed(), subnet.netmask_packed())) return (tuple(subnets[socket.AF_INET]), tuple(subnets[socket.AF_INET6])) def resolveZones(name_list): """ Helper function to convert a list of zone names to a list of Zone instnaces """ name_list = makeSequence(name_list) for name in name_list: if Zone.lookup_by_name(name) == None: raise ValueError, "No zone was defined with that name; zone='%s'" % (name,) def parsePorts(port_list): """ Helper function to convert a port or port range list to a list of port ranges. Accepted input formats are: (port1, port2, port3) - list of ports (port1, (begin, end), port3) - list of ports mixed with ranges """ ports = [] port_list = makeSequence(port_list) for item in port_list: if isinstance(item, PortRange): ports.append(item.getTuple()) else: if isinstance(item, basestring): item = int(item) if not isinstance(item, int): raise ValueError, "Integer port value expected; value='%s'" % (item,) ports.append((item, item)) return ports # store id self._id = kw.pop('rule_id', None) # store service service_name = kw.pop('service', None) self._service = Globals.services.get(service_name, None) if not self._service: raise ValueError, "No valid service was specified for the rule; service='%s'" % (service_name,) # convert and check special dimensions: subnets, ports and zones at the moment (kw['src_subnet'], kw['src_subnet6']) = parseSubnets(kw.get('src_subnet', [])) (kw['dst_subnet'], kw['dst_subnet6']) = parseSubnets(kw.get('dst_subnet', [])) kw['src_port'] = parsePorts(kw.get('src_port', [])) kw['dst_port'] = parsePorts(kw.get('dst_port', [])) resolveZones(kw.get('src_zone', [])) resolveZones(kw.get('dst_zone', [])) # store values specified self._dimensions = {} for key, value in kw.items(): if key not in self.valid_dimensions: raise ValueError, "Unknown dimension '%s'" % (key,) self._dimensions[key] = makeSequence(value) Globals.rules.add(self) Dispatch.RuleDispatcher.createOneInstance() def getId(self): return self._id def setId(self, rule_id): self._id = rule_id def buildKZorpMessage(self, dispatcher_name): messages = [] # determine maximum dimension length kzorp_dimensions = {} for (key, value) in self._dimensions.items(): kzorp_dimensions[self.valid_dimensions[key]] = value kzorp_dimension_sizes = dict(map(lambda (key, value): (key, len(value)), kzorp_dimensions.items())) max_dimension_length = max(kzorp_dimension_sizes.values()) if len(kzorp_dimension_sizes) > 0 else 0 messages.append((kznf.KZNL_MSG_ADD_RULE, kznf.create_add_n_dimension_rule_msg(dispatcher_name, self.getId(), self._service.name, kzorp_dimension_sizes))) for i in xrange(max_dimension_length): data = {} for dimension, values in kzorp_dimensions.items(): if len(values) > i: data[dimension] = values[i] messages.append((kznf.KZNL_MSG_ADD_RULE_ENTRY, kznf.create_add_n_dimension_rule_entry_msg(dispatcher_name, self.getId(), data))) return messages zorp-3.9.5/pylib/Zorp/Service.py000066400000000000000000001223531172670260400165600ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Service module defines the classes used to create service definitions. This module defines classes encapsulating service descriptions. Zorp services define how incoming connection requests are handled. When a connection is accepted by a Dispatchers, the service bound to the Dispatcher creates an instance of itself. This instance handles the connection and proxies the traffic between the client and the server. The instance of the selected service is created using the 'startInstance()' method. A service does not perform useful activity on its own, it needs a Dispatcher to bind the service to a network interface of the firewall. New instances of the service are started as the Dispatcher accepts new connections.
Naming services The name of the service must be a unique identifier; dispatchers refer to this unique ID. Use clear, informative, and consistent service names. Include the following information in the service name: Source zones, indicating which clients may use the service (e.g., intranet). The protocol permitted in the traffic (e.g., HTTP). Destination zones, indicating which servers may be accessed using the service (e.g., Internet). Name the service that allows internal users to browse the Web intra_HTTP_internet. Use dots to indicate child zones, e.g., intra.marketing_HTTP_inter.
Determining the server and client zone The client's IP address identifies a client zone and the access control information associated by the client zone determines whether a service identified by a given name is permitted. Similarly when the server side connection is established the same service name is used to determine whether the service is permitted to target a server in the zone of the server.
""" from Stream import Stream from Session import StackedSession from Zorp import * from Chainer import ConnectChainer from Router import TransparentRouter, DirectedRouter from Auth import AuthPolicy, getAuthPolicyObsolete, getAuthenticationPolicy from Resolver import DNSResolver, getResolverPolicy, ResolverPolicy from NAT import getNATPolicy, NATPolicy, NAT_SNAT, NAT_DNAT from Encryption import getEncryptionPolicy from Exceptions import LimitException import types, thread, time, socket import kznf.kznfnetlink default_snat = None default_dnat = None default_auth = None default_router = None default_chainer = None class AbstractService(object): """ Class encapsulating the abstract Service properties. AbstractService implements an abstract service. Service definitions should be based on a customized class derived from AbstractService, or on the predefined Service class. name The name of the service. """ def __init__(self, name): """ Constructor to initialize an instance of the AbstractService class. This constructor creates an AbstractService instance and sets the attributes of the instance according to the received arguments. It also registers the Service to the services hash so that dispatchers can find the service instance. name The name of the service. """ if Globals.services.has_key(name): raise ValueError, "Duplicate service: %s" % name Globals.services[name] = self self.name = name def startInstance(self, session): """ Function to start an instance of this service. Abstract method to be implemented in derived classes. Should start an instance of the given service. A service instance takes care of the client connection, connects to the server and supervises the traffic going in either direction. Tasks of a service instance are implemented by classes derived from Proxy. This method unconditionally raises a NotImplementedError exception to indicate that it must be overridden by descendant classes like 'Service'. session start service within this session """ raise NotImplementedError def stopInstance(self, session): """ Function called when an instance of this service is ended This function is called by Session.__del__ and indicates that a given session (instance) of this service is ended. session ending session """ raise NotImplementedError def __str__(self): """ Function to represent this object as a string This function is called by the Python core when this object is used as-, or casted to a string. It simply returns the service name. """ return self.name class Service(AbstractService): """ Class encapsulating a service definition. A service is one of the fundamental objects in Zorp. It stores the names of proxy related parameters, and is also used for access control purposes to decide what kind of traffic is permitted. The Service class transfers application-level (proxy) services. To transfer connections on the packet-filter level, use the PFService class. Service example The following service transfers HTTP connections. Every parameter is left at its default. Service(name="demo_http, proxy_class=HttpProxy, router=TransparentRouter(forge_addr=FALSE)) The following service handles HTTP connections. This service uses authentication and authorization, and network address translation on the client addresses (SNAT). Service(name="demo_http", proxy_class=HttpProxy, authentication_policy="demo_authentication_policy", authorization_policy="demo_permituser", snat_policy="demo_natpolicy", router=TransparentRouter(overrideable=FALSE, forge_addr=FALSE)) The following example defines the Zorp classes required for a service to work: the client and server zones and the services they can accept; the dispatcher that starts the service, and the service itself. InetZone('internet', ['0.0.0.0/0'], inbound_services=[ "office_http_inter"], outbound_services=[]) InetZone('office', ['192.168.1.0/32', '192.168.2.0/32'], outbound_services=[ "office_http_inter"]) def demo_instance() : Service(name="office_http_inter", proxy_class=HttpProxy, router=TransparentRouter(forge_addr=FALSE)) Dispatcher(transparent=TRUE, bindto=DBIface(protocol=ZD_PROTO_TCP, iface="eth0", ip="192.168.1.1", port=50080), service="office_http_inter") router A router instance used to determine the destination address of the server. See for details. chainer A chainer instance used to connect to the destination server. See for details. snat_policy Name of the NAT policy instance used to translate the source addresses of the sessions. See for details. dnat_policy Name of the NAT policy instance used to translate the destination addresses of the sessions. See for details. proxy_class Name of the proxy class instance used to analyze the traffic transferred in the session. See for details. authentication_policy Name of the AuthenticationPolicy instance used to authenticate the clients. See for details. authorization_policy Name of the AuthorizationPolicy instance used to authorize the clients. See for details. auth_name Authentication name of the service. This string informs the users of the Zorp Authentication Agent about which service they are authenticating for. Default value: the name of the service. resolver_policy Name of the ResolvePolicy instance used to resolve the destination domain names. See for details. Default value: DNSResolver max_instances Permitted number of concurrent instances of this service. Usually each service instance handles one connection. The default value is 0, which allows unlimited number of instances. max_sessions Maximum number of concurrent sessions handled by one thread. num_instances The current number of running instances of this service. instance_id The sequence number of the last session started keepalive The TCP keepalive option, one of the Z_KEEPALIVE_NONE, Z_KEEPALIVE_CLIENT, Z_KEEPALIVE_SERVER, Z_KEEPALIVE_BOTH values. """ keepalive = Z_KEEPALIVE_NONE def __init__(self, name, proxy_class, router=None, chainer=None, snat_policy=None, snat=None, dnat_policy=None, dnat=None, authentication_policy=None, authorization_policy=None, max_instances=0, max_sessions=0, auth_name=None, resolver_policy=None, auth=None, auth_policy=None, keepalive=None, encryption_policy=None): """ Constructor to initialize a Service instance. This contructor defines a Service with the specified parameters. name The name identifying the service. router None Name of the router instance used to determine the destination address of the server. Defaults to TransparentRouter if no other router is specified. chainer None Name of the chainer instance used to connect to the destination server. Defaults to ConnectChainer if no other chainer is specified. snat_policy None Name of the NAT policy instance used to translate the source addresses of the sessions. See for details. snat None Obsolete parameter, use snat_policy instead. dnat_policy None Name of the NAT policy instance used to translate the destination addresses of the sessions. See for details. dnat None Obsolete parameter, use dnat_policy instead. proxy_class Name of the proxy class instance used to analyze the traffic transferred in the session. See for details. authentication_policy None Name of the AuthenticationPolicy instance used to authenticate the clients. See for details. authorization_policy None Name of the AuthorizationPolicy instance used to authorize the clients. See for details. auth None Obsolete parameter, use authentication_policy instead. auth_policy None Obsolete parameter, use authorization_policy instead. auth_name None Authentication name of the service. This string informs the users of the Zorp Authentication Agent about which service they are authenticating for. Default value: the name of the service. max_instances 0 Permitted number of concurrent instances of this service. Usually each service instance handles one connection. Default value: 0 (unlimited). . max_sessions Maximum number of concurrent sessions handled by one thread. resolver_policy None Name of the ResolvePolicy instance used to resolve the destination domain names. See for details. Default value: DNSResolver. . keepalive The TCP keepalive option, one of the Z_KEEPALIVE_NONE, Z_KEEPALIVE_CLIENT, Z_KEEPALIVE_SERVER, Z_KEEPALIVE_BOTH values. """ super(Service, self).__init__(name) self.proxy_class = proxy_class self.router = router or default_router or TransparentRouter() self.chainer = chainer or default_chainer or ConnectChainer() if (snat or default_snat) and snat_policy: raise ValueError, "Cannot set both snat and snat_policy" if (dnat or default_dnat) and dnat_policy: raise ValueError, "Cannot set both dnat and dnat_policy" if (auth or default_auth or auth_policy) and authentication_policy: raise ValueError, "Cannot set authentication_policy and auth or auth_policy" if snat or default_snat: self.snat_policy = NATPolicy('__%s-snat' % name, snat or default_snat) else: self.snat_policy = getNATPolicy(snat_policy) if dnat or default_dnat: self.dnat_policy = NATPolicy('__%s-dnat' % name, dnat or default_dnat) else: self.dnat_policy = getNATPolicy(dnat_policy) if type(auth) == types.StringType: auth_policy = auth auth = None if keepalive: self.keepalive = keepalive if auth_policy: # one older auth_policy implementation (up to Zorp 3.0) auth_policy = getAuthPolicyObsolete(auth_policy) self.authentication_policy = auth_policy.getAuthenticationPolicy() elif auth or default_auth: # even older auth implementation (up to Zorp 2.1) auth_policy = AuthPolicy(None, auth or default_auth) self.authentication_policy = auth_policy.getAuthenticationPolicy() else: # current Authentication support self.authentication_policy = getAuthenticationPolicy(authentication_policy) self.auth_name = auth_name or name if resolver_policy: self.resolver_policy = getResolverPolicy(resolver_policy) else: self.resolver_policy = ResolverPolicy(None, DNSResolver()) if encryption_policy: self.encryption_policy = getEncryptionPolicy(encryption_policy) else: self.encryption_policy = None self.max_instances = max_instances self.max_sessions = max_sessions self.num_instances = 0 self.proxy_group = ProxyGroup(self.max_sessions) self.lock = thread.allocate_lock() def startInstance(self, session): """ Start a service instance. Called by the Listener to create an instance of this service. session The session object """ if self.max_instances != 0 and self.num_instances >= self.max_instances: raise LimitException, "Instance limit reached" self.lock.acquire() self.num_instances = self.num_instances + 1 self.lock.release() session.started = 1 session.name = self.name instance_id = getInstanceId(self.name) # NOTE: the instance id calculation is now based in C to create # unique session IDs even after policy reload # instance_id = self.instance_id # self.instance_id = self.instance_id + 1 session.setServiceInstance(instance_id) timestamp = str(time.time()) szigEvent(Z_SZIG_SERVICE_COUNT, (Z_SZIG_TYPE_PROPS, (self.name, { 'session_number': instance_id + 1, 'sessions_running': self.num_instances, 'last_started': timestamp, } ))) szigEvent(Z_SZIG_CONNECTION_PROPS, (Z_SZIG_TYPE_CONNECTION_PROPS, (self.name, instance_id, 0, 0, { 'started': timestamp, 'session_id': session.session_id, 'proxy_module': self.proxy_class.name, 'proxy_class': self.proxy_class.__name__, 'client_address': str(session.client_address), 'client_local': str(session.client_local), 'client_zone': session.client_zone.getName(), } ))) szigEvent(Z_SZIG_CONNECTION_START, (Z_SZIG_TYPE_PROPS, (self.name, {} ))) ## LOG ## # This message reports that a new proxy instance is started. ## log(session.session_id, CORE_SESSION, 3, "Starting proxy instance; client_fd='%d', client_address='%s', client_zone='%s', client_local='%s', client_protocol='%s'", (session.client_stream.fd, session.client_address, session.client_zone, session.client_local, session.protocol_name)) ss = StackedSession(session, self.chainer) session.client_stream.name = session.session_id + '/' + self.proxy_class.name + '/client' proxy = self.proxy_class(ss) if not self.proxy_group.start(proxy): self.proxy_group = ProxyGroup(self.max_sessions) if not self.proxy_group.start(proxy): raise RuntimeError, "Error starting proxy in group" return TRUE def stopInstance(self, session): """ Function called when a session terminates. This function is called when a session terminates. It decrements concurrent session count. session session we belong to """ if session.started: self.lock.acquire() self.num_instances = self.num_instances - 1 self.lock.release() szigEvent(Z_SZIG_SERVICE_COUNT, (Z_SZIG_TYPE_PROPS, (self.name, { 'sessions_running': self.num_instances, } ))) szigEvent(Z_SZIG_CONNECTION_STOP, (Z_SZIG_TYPE_CONNECTION_PROPS, (self.name, session.instance_id, 0, 0, {}))) ## LOG ## # This message reports that a new proxy instance is stopped. ## log(session.session_id, CORE_SESSION, 4, "Ending proxy instance;") def buildKZorpMessage(self): """ """ return [(kznf.kznfnetlink.KZNL_MSG_ADD_SERVICE, kznf.kznfnetlink.create_add_proxyservice_msg(self.name))]; class PFService(AbstractService): """ Class encapsulating a packet-filter service definition. The PFService class transfers packet-filter level services. To transfer connections on the application-level (proxy), use the PFService class. PFService example The following packet-filtering service transfers TCP connections that arrive to port 5555. PFService(name="intranet_PF5555_internet", router=TransparentRouter(forge_addr=FALSE)) The following example defines the Zorp classes required for a service to work: the client and server zones and the services they can accept; the dispatcher that starts the service, and the service itself. InetZone('internet', ['0.0.0.0/0'], inbound_services=[ "intranet_PF5555_internet"]) InetZone('intranet', [], outbound_services=[ "intranet_PF5555_internet"]) def demo() : PFService(name="intranet_PF5555_internet", router=TransparentRouter(forge_addr=FALSE)) Dispatcher(transparent=TRUE, bindto=DBIface(protocol=ZD_PROTO_TCP, port=55555, iface="eth0", ip="192.168.0.15"), rule_port="55555", service="intranet_PF5555_internet") router A router instance used to determine the destination address of the server. See for details. snat_policy Name of the NAT policy instance used to translate the source addresses of the sessions. See for details. dnat_policy Name of the NAT policy instance used to translate the destination addresses of the sessions. See for details. """ def __init__(self, name, router=None, snat_policy=None, dnat_policy=None): """ Constructor to initialize a PFService instance. This constructor defines a packetfilter-service with the specified parameters. """ super(PFService, self).__init__(name) self.router = router or default_router or TransparentRouter() self.snat_policy = getNATPolicy(snat_policy) self.dnat_policy = getNATPolicy(dnat_policy) def buildKZorpMessage(self): """ """ def addNATMappings(messages, nat_type, nat_policy): if nat_type == NAT_SNAT: msg_type = kznf.kznfnetlink.KZNL_MSG_ADD_SERVICE_NAT_SRC else: msg_type = kznf.kznfnetlink.KZNL_MSG_ADD_SERVICE_NAT_DST if nat_policy: nat_mapping = nat_policy.getKZorpMapping() for mapping in nat_mapping: messages.append((msg_type, kznf.kznfnetlink.create_add_service_nat_msg(self.name, mapping))) if isinstance(self.router, TransparentRouter): flags = kznf.kznfnetlink.KZF_SVC_TRANSPARENT router_target_family = None router_target_ip = None router_target_port = None elif isinstance(self.router, DirectedRouter): if len(self.router.dest_addr) > 1: raise ValueError, "DirectedRouter with more than one destination address not supported by KZorp" flags = 0 router_target_family = self.router.dest_addr[0].family router_target_ip = self.router.dest_addr[0].pack() router_target_port = self.router.dest_addr[0].port else: raise ValueError, "Invalid router type specified for port forwarded service" if self.router.forge_addr: flags = flags | kznf.kznfnetlink.KZF_SVC_FORGE_ADDR messages = [] messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_SERVICE, kznf.kznfnetlink.create_add_pfservice_msg(self.name, \ flags, router_target_family, router_target_ip, router_target_port))) if self.snat_policy: addNATMappings(messages, NAT_SNAT, self.snat_policy) if self.dnat_policy: addNATMappings(messages, NAT_DNAT, self.dnat_policy) return messages zorp-3.9.5/pylib/Zorp/Session.py000066400000000000000000000723321172670260400166040ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the session related classes. This module defines the abstract session interface in a class named AbstractSession, and two descendants MasterSession and StackedSession. Sessions are hierarchically stacked into each other just like proxies. All sessions except the master session have a parent session from which child sessions inherit variables. Child sessions are stacked into their master sessions, so stacked sessions can inherit data from the encapsulating proxy instances. (Inheritance is implemented using a simple getattr wrapper.) Instances of the Session classes store the parameters of the client-side and server-side connections in a session object (for example, the IP addresses and zone of the server and the client, and the username and group memberships of the user when authentication is used). Other components of Zorp refer to this data when making various policy-based decisions. """ import Zorp from Zorp import * from Zone import Zone from Cache import ShiftCache inbound_cache = ShiftCache('inbound_cache', config.options.inbound_service_cache_threshold) outbound_cache = ShiftCache('outbound_cache', config.options.outbound_service_cache_threshold) class AbstractSession(object): """ Class encapsulating an abstract session for different types (master, or stacked). Abstract base class for different session types (master, or stacked), both MasterSession and StackedSession are derived from this class. """ def destroy(self): """ Method called at session destruction time. This method is called when the session is being destroyed. We close filedescriptors here in case no proxy module could be started (because of policy violations, or because the module cannot be found). """ if self.client_stream: self.client_stream.close() if self.server_stream: self.server_stream.close() class MasterSession(AbstractSession): """ Class encapsulating a master session. This class encapsulates a master session that is on the top of the session hierarchy.
Referencing attributes exported by parent proxies When a stacked proxy needs some information exported by its parent, it can simply use the by-name references in the session structure. For example a proxy named 'pssl' will export an attribute named 'pssl' in its session which is inherited in the session hierarchy, so a stacked proxy can refer to any pssl attributes through this reference: Referencing parent proxy attributes by type class MyPsslProxy(PsslProxy): class EmbeddedHttpProxy(HttpProxy): def config(self): super(MyPsslProxy, self).config() peer = self.session.pssl.server_peer_certificate.subject def config(self): super(MyPsslProxy, self).config() self.stack_proxy = self.EmbeddedHttpProxy
client_stream Client-side stream. client_address IP address of the client. client_local The IP address of the server targeted by the client. client_zone Zone of the client. server_stream Server-side stream. server_address The IP address Zorp connects to. Most often this is the IP address requested by the client, but Zorp can redirect the client requests to different IPs. server_local Zorp connects the server from this IP address. This is either the IP address of Zorp's external interface, or the IP address of the client (if Forge Port is enabled). The client's original IP address may be modified if SNAT policies are used. server_zone Zone of the server. target_address The IP address Zorp connects to. Most often this is the IP address requested by the client, but Zorp can redirect the client requests to different IPs. target_local Zorp connects the server from this IP address. This is either the IP address of Zorp's external interface, or the IP address of the client (if Forge Port is enabled). The client's original IP address may be modified if SNAT policies are used. target_zone Zone of the server. target_address_inband destination address is determined by the proxy target_local_loose BOOLEAN Allow loosely allocated source ports. (e.g. it is not absoletely necessary to allocate the same port as specified in server_localparameter>, it is enough if it matches its category.) target_local_random BOOLEAN Allocate source ports randomly using a cryptographically secure algorithm. target_local_loose should also be enabled for this. service The name of the service which started this session. session_id A unique identifier for this session using the following format: (Zorp_hostname/service:instance id/proxy). instance_id The instance identifier of the service (sequence number). started Indicates that the instance has been started. auth_user The username of the authenticated user. auth_groups List of groups the authenticated user is member of. authorized Stores whether the session was authorized. protocol The protocol used in the client-side connection, represented as an integer. protocol_name The name of the protocol used in the client-side connection.
""" def __init__(self): """ Constructor to initialize a MasterSession instance. This constructor initializes a new MasterSession instance based on its arguments. """ self.base_session_id = 'svc' self.session_id = self.base_session_id self.client_stream = None self.client_address = None self.client_local = None self.client_zone = None self.server_stream = None self.server_address = None self.server_local = None self.server_zone = None self.target_address = () self.target_local = None self.target_zone = () self.target_address_inband = FALSE self.target_local_loose = TRUE self.target_local_random = FALSE self.auth_user = "" self.auth_groups = () self.authorized = FALSE self.started = 0 self.service = None self.instance_id = 0 self.setProtocol(0) self.proxy = None def __del__(self): """ Function called when the master session is freed. This function is called when the master session is freed, thus the session ended. We inform our spawner service about this event. """ if self.service: self.service.stopInstance(self) def setProtocol(self, protocol): """ Sets the server-side protocol. This function is called by the dispatcher callbacks to specify the protocol that was used to establish the client side connection. This function stores this value in the current session. protocol INTEGER protocol identifier, one of ZD_PROTO_* constants """ self.protocol = protocol try: self.protocol_name = ZD_PROTO_NAME[protocol] except KeyError: self.protocol_name = "Unknown(%d)" % (self.protocol) def setService(self, service): """ Sets the service belonging to this session. Stores the service reference, and recalculates the session_id. This is called by the Listener after the service is determined. service SERVICE Service instance """ self.service = service self.session_id = "%s/%s" % (self.base_session_id, service.name) ## LOG ## # This message reports that the given service is started, because of a new connection. ## log(self.session_id, CORE_SESSION, 5, "Starting service; name='%s'", service.name) def setClientAddress(self, addr): self.client_address = addr self.client_zone = Zone.lookup(addr) def setServerAddress(self, addr): self.server_address = addr self.server_zone = Zone.lookup(addr) def setTargetAddress(self, addr): """ Set the target server address. This is a compatibility function for proxies that override the routed target. addr Server address """ # NOTE: handling SockAddr types is a compatibility hack, as # proxies might call setServer with a SockAddr instance # instead of a tuple of SockAddrs if isinstance(addr, SockAddrType): self.target_address = (addr,) else: self.target_address = addr self.target_zone = [] for a in self.target_address: self.target_zone.append(Zone.lookup(a)) setServer = setTargetAddress def isClientPermitted(self): """ Function to actually check access control. This function is called when a connection is established to perform access control checks whether the client is permitted to use the requested service. Its return value specifies the result of the check. Returns ZV_ACCEPT for success, and ZV_REJECT for failure. """ global outbound_cache zone_name = self.client_zone.getName() cached = outbound_cache.lookup((zone_name, self.service.name)) if cached == ZV_REJECT: ## LOG ## # This message indicates that because of a cached decision this service is not permitted as an outbound service from that zone. # It means that the client from that zone tried to use this service and it is not permitted to do so. # Check that the service is included in the outbound_services set of the Zone. # @see: Zone ## log(self.session_id, CORE_POLICY, 1, "Outbound service not permitted (cached); service='%s', client_zone='%s', client='%s', server_zone='%s', server='%s'", (self.service, self.client_zone, self.client_address, self.server_zone, self.server_address)) elif cached: return cached if self.client_zone.isOutboundServicePermitted(self.service) != ZV_ACCEPT: outbound_cache.store((zone_name, self.service.name), ZV_REJECT) ## LOG ## # This message indicates that a service going out from the given # zone was denied by the policy. Check that the service is included in # the outbound_services set of the Zone. ## log(self.session_id, CORE_POLICY, 1, "Outbound service not permitted; service='%s', client_zone='%s', client='%s', server_zone='%s', server='%s'", (self.service, self.client_zone, self.client_address, self.server_zone, self.server_address)) return ZV_REJECT outbound_cache.store((zone_name, self.service.name), ZV_ACCEPT) return ZV_ACCEPT def isServerPermitted(self): """ Function to actually check access control. This function is called when a connection is to be established with the server. It performs access control checks whether the connection to the server is permitted by the policy. Its return value specifies the result of the check. Returns ZV_ACCEPT for success, and ZV_REJECT for failure. """ global inbound_cache zone_name = self.server_zone.getName() cached = inbound_cache.lookup((zone_name, self.service.name)) if cached == ZV_REJECT: ## LOG ## # This message indicates that because of a cached decision this service is not permitted as an inbound service to that zone. # It means that this service tried to connect to a server in that zone and it is not permitted to do so. # Check that the service is included in the inbound_services set of the Zone. # @see: Zone ## log(self.session_id, CORE_POLICY, 1, "Inbound service not permitted (cached); service='%s', client_zone='%s', client='%s', server_zone='%s', server='%s'", (self.service, self.client_zone, self.client_address, self.server_zone, self.server_address)) elif cached: return cached if self.server_zone.isInboundServicePermitted(self.service) != ZV_ACCEPT: inbound_cache.store((zone_name, self.service.name), ZV_REJECT) ## LOG ## # This message indicates that a service trying to enter to the given # zone was denied by the policy. Check that the service is included in # the inbound_services set of the Zone. ## log(self.session_id, CORE_POLICY, 1, "Inbound service not permitted; service='%s', client_zone='%s', client='%s', server_zone='%s', server='%s'", (self.service, self.client_zone, self.client_address, self.server_zone, self.server_address)) return ZV_REJECT inbound_cache.store((zone_name, self.service.name), ZV_ACCEPT) return ZV_ACCEPT def setServiceInstance(self, instance_id): """ Set service instance number and recalculate session id. Sets service instance number, and makes up a unique identifier for this session. instance_id unique identifier of the service instance """ self.instance_id = instance_id self.session_id = "%s/%s:%d" % (self.base_session_id, self.name, self.instance_id) self.master_session_id = self.session_id class StackedSession(AbstractSession): """ Class encapsulating a subsession. This class represents a stacked session, e.g., a session within the session hierarchy. Every subsession inherits session-wide parameters from its parent. owner The parent session of the current session. chainer The chainer used to connect to the parent proxy. If unset, the server_stream parameter must be set. """ def __init__(self, owner, chainer = None): """ Constructor to initialize a StackedSession instance. This constructor initializes a new StackedSession instance based on parameters. owner Parent session chainer Chainer used to chain up to parent. """ self.owner = owner self.chainer = chainer def __getattr__(self, name): """ Function to perform attribute inheritance. This function is called by the Python core when an attribute is referenced. It returns variables from the parent session, if not overriden here. Returns The value of the given attribute. name Name of the attribute to get. """ try: if name != '__dict__': return self.__dict__[name] else: raise KeyError except KeyError: return getattr(self.owner, name) def setProxy(self, proxy): """ Set the proxy name used in this subsession. Stores a reference to the proxy class, and modifies the session_id to include the proxy name. This is called by the Listener after the proxy module to use is determined. proxy Proxy class, derived from Proxy """ self.session_id = "%s/%s:%d/%s" % (self.base_session_id, self.name, self.instance_id, proxy) zorp-3.9.5/pylib/Zorp/SockAddr.py000066400000000000000000000226641172670260400166560ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the SockAddr. This module implements inet_ntoa and inet_aton. The module also provides an interface to the SockAddr services of the Zorp core. SockAddr is used for example to define the bind address of Dispatchers, or the address of the ZAS server in AuthenticationProvider policies. """ class SockAddrInet(object): """ Class encapsulating an IPv4 address:port pair. This class encapsulates an IPv4 address:port pair, similarly to the sockaddr_in struct in C. The class is implemented and exported by the Zorp core. The SockAddrInet Python class serves only documentation purposes, and has no real connection to the behavior implemented in C. SockAddrInet example The following example defines an IPv4 address:port pair. SockAddrInet('192.168.10.10', 80) The following example uses SockAddrInet in a dispatcher. See for details on Dispatchers. Dispatcher(transparent=TRUE, bindto=DBSockAddr(protocol=ZD_PROTO_TCP, sa=SockAddrInet('192.168.11.11', 50080)), service="intra_HTTP_inter", backlog=255, rule_port="50080") type The inet value that indicates an address in the AF_INET domain. ip IP address (network byte order). ip_s IP address in string representation. port Port number (network byte order). """ pass class SockAddrInet6(object): """ Class encapsulating an IPv6 address:port pair. This class encapsulates an IPv6 address:port pair, similarly to the sockaddr_in struct in C. The class is implemented and exported by the Zorp core. The SockAddrInet Python class serves only documentation purposes, and has no real connection to the behavior implemented in C. SockAddrInet example The following example defines an IPv6 address:port pair. SockAddrInet('fec0::1', 80) The following example uses SockAddrInet in a dispatcher. See for details on Dispatchers. Dispatcher(transparent=TRUE, bindto=DBSockAddr(protocol=ZD_PROTO_TCP, sa=SockAddrInet('fec0::1', 50080)), service="intra_HTTP_inter", backlog=255, rule_port="50080") type The inet value that indicates an address in the AF_INET domain. ip IP address (network byte order). ip_s IP address in string representation. port Port number (network byte order). """ pass class SockAddrInetRange(object): """ Class encapsulating an IPv4 address and a port range. A specialized SockAddrInet class which allocates a new port within the given range of ports when a dispatcher bounds to it. The class is implemented and exported by the Zorp core. The SockAddrInetRange Python class serves only documentation purposes, and has no real connection to the behavior implemented in C. type The inet value that indicates an address in the AF_INET domain. ip IP address (network byte order). ip_s IP address in string representation. port Port number (network byte order). """ pass class SockAddrUnix(object): """ Class encapsulating a UNIX domain socket. This class encapsulates a UNIX domain socket endpoint. The socket is represented by a filename. The SockAddrUnix Python class serves only documentation purposes, and has no real connection to the behavior implemented in C. SockAddrUnix example The following example defines a Unix domain socket. SockAddrUnix('/var/sample.socket') The following example uses SockAddrUnix in a DirectedRouter. Service(name="demo_service", proxy_class=HttpProxy, router=DirectedRouter(dest_addr=SockAddrUnix('/var/sample.socket'), overrideable=FALSE, forge_addr=FALSE)) type The unix value that indicates an address in the UNIX domain. """ zorp-3.9.5/pylib/Zorp/Stack.py000066400000000000000000000236701172670260400162270ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ The Stack module defines the classes used to connect to a stacking provider. Zorp is capable of stacking, that is, handing over parts of the traffic to other modules for further inspection (e.g., to other proxies to inspect embedded protocols, to content vectoring modules for virus filtering, etc.). The Stack module defines the classes required for this functionality. Stacking in Zorp services is performed using StackingProvider policies, which reference the host that performs the stacked operations using the RemoteStackingBackend class. """ from Zorp import * import Globals class StackingProvider(object): """ This is a policy class that is used to reference a configured stacking provider in service definitions. Instances of the StackingProvider class are policies that define which remote stacking backend a particular service uses to inspect the contents of the traffic. A simple StackingProvider class The following class creates a simple stacking provider that can be referenced in service definitions. The remote host that provides the stacking services is located under the 192.168.12.12 IP address. StackingProvider(name="demo_stackingprovider", backend=RemoteStackingBackend(addrs=(SockAddrInet('192.168.12.12', 1318),))) Using a StackingProvider in an FTP proxy The following classes define a stacking provider that can be accesses a local ZCV instance using a domain socket. This service provider is then used to filter FTP traffic. The configuration of the ZCV (i.e., what modules it uses to filter the traffic is not discussed here). class StackingFtpProxy(FtpProxy): def config(self): super(StackingFtpProxy, self).config() self.request_stack["RETR"]=(FTP_STK_DATA, (Z_STACK_PROVIDER, "demo_stackingprovider", "default_rulegroup")) StackingProvider(name="demo_stackingprovider_socket", backend=RemoteStackingBackend(addrs=(SockAddrUnix('/var/run/zcv/zcv.sock'),))) """ def __init__(self, name, backend): """ Constructor to initialize an instance of the StackingProvider class. This constructor creates a StackingProvider instance and sets the attributes of the instance according to the received arguments. name Name of the Stacking provider policy. This name can be referenced in the service definitions. backend A configured RemoteStackingBackend class containing the address of the remote stacking backend, e.g., RemoteStackingBackend(addrs=(SockAddrInet('192.168.2.3', 1318),)) or RemoteStackingBackend(addrs=(SockAddrUnix('/var/run/zcv/zcv.sock'),)). . """ if Globals.stacking_providers.has_key(name): raise ValueError, "Duplicate Stacking provider: %s" % name Globals.stacking_providers[name] = self self.name = name self.backend = backend def getStackingProviderBackend(name): """ """ if name: if Globals.stacking_providers.has_key(name): return Globals.stacking_providers[name].backend else: log(None, CORE_POLICY, 3, "No such stacking provider; provider='%s'", (name)) return None class AbstractStackingBackend(object): """ This is an abstract class, currently without any functionality. This is an abstract class, currently without any functionality. """ def __init__(self): """ """ pass def stack(self): """ """ raise IOError, "Unimplemented stacking method" class RemoteStackingBackend(AbstractStackingBackend): """ Constructor to initialize an instance of the RemoteStackingBackend class. This class contains the address of the host that performs the stacked operations. It is typically used to access the Zorp Content Vectoring Server (ZCV) to perform virus filtering in the traffic. The remote backend can be accessed using the TCP protocol or a local socket, e.g., RemoteStackingBackend(addrs=(SockAddrInet('192.168.2.3', 1318),)) or RemoteStackingBackend(addrs=(SockAddrUnix('/var/run/zcv/zcv.sock'),)). . """ def __init__(self, addrs): """ addrs The address of the remote backend in SockAddrInet or SockAddrUnix format. Separate addresses with commas to list more than one address for a backend. Zorp will connect to these addresses in a failover fashion. """ super(RemoteStackingBackend, self).__init__() self.addrs = addrs self.current_host = 0 def stack(self, stack_info): """ """ for i in range(len(self.addrs)): addr = self.addrs[self.current_host] self.current_host = (self.current_host + 1) % len(self.addrs) try: return performStackRemote(addr, stack_info) except IOError: log(None, CORE_ERROR, 3, "Error performing stack handshake with peer; addr='%s'", str(addr)) log(None, CORE_DEBUG, 6, "Could not finish handshake with any of the stacking peers;") return None zorp-3.9.5/pylib/Zorp/Stream.py000066400000000000000000000133471172670260400164150ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the streaming component. This module defines the Stream class, encapsulating file descriptors and related functions. """ G_IO_STATUS_ERROR = 0 G_IO_STATUS_NORMAL = 1 G_IO_STATUS_EOF = 2 G_IO_STATUS_AGAIN = 3 class Stream(object): """ Class encapsulating the file descriptor and related functions. This class encapsulates a full-duplex data tunnel, represented by a UNIX file descriptor. Proxies communicate with its peers through instances of this class. The client_stream and server_stream attributes of the Session class contain a Stream instance. fd The file descriptor associated to the stream. name The name of the stream. bytes_recvd The number of bytes received in the stream. bytes_sent The number of bytes sent in the stream. """ def __init__(self, fd, name): """ Constructor to initialize a stream. This constructor initializes a Stream instance setting its attributes according to arguments. fd The file descriptor associated to the stream. name The name of the stream. """ pass def read(self, count): """ Method to read up to count bytes from the stream. This method reads up to count bytes from the stream and returns it as a . count maximum number of bytes to read """ pass def write(self, buf): """ Method to write the contents of a buffer to the stream. This method writes the contents of the given buffer to the stream. buf buffer to write """ zorp-3.9.5/pylib/Zorp/Subnet.py000066400000000000000000000300571172670260400164170ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to address domains. This module implements the Subnet classes, which encapsulate a set of physical addresses. """ from Zorp import * from string import split, atoi from socket import htonl, ntohl, inet_ntoa, inet_aton, inet_pton, inet_ntop, ntohs import socket import struct def packed_1operand(a, f): """apply second argument to each character of the packed string 'a', converted to int""" return map(lambda x: chr(f(ord(x)) & 0xff), a) def packed_2operand(a, b, f): """apply the third argument to each character in both first and second arguments """ return "".join(map(lambda t: chr(f(ord(t[0]), ord(t[1]))), zip(a, b))) def packed_mask(addr, mask): """ """ return packed_2operand(addr, mask, lambda a, b: a & b) class Subnet(object): """ """ def __init__(self): """ """ pass @staticmethod def create(addr): """ Factory function for InetSubnet and Inet6Subnet instances -- guess based on the string whether or not the subnet is IPv6 or IPv4. """ if '.' in addr: return InetSubnet(addr) elif ':' in addr: return Inet6Subnet(addr) class InetSubnet(Subnet): """ Class representing IPv4 address ranges. """ def __init__(self, addr): """ Constructor to initialize an InetSubnet instance A class representing Internet (IPv4) addresses, and IP segments. The address is represented in the XXX.XXX.XXX.XXX/M format, where XXX.XXX.XXX.XXX is the network address, and M specifies the number of ones (1) in the netmask. addr The string representation of an address, optionally with a subnet prefix length. """ super(InetSubnet, self).__init__() parts = split(addr,'/') try: self.mask_bits = atoi(parts[1]) except IndexError: self.mask_bits = 32 self.mask = struct.pack(">I", ((1 << self.mask_bits) - 1) << (32 - self.mask_bits)) self.ip = inet_aton(parts[0]) def __str__(self): """ Function returning the string representation of this instance. """ return "%s/%u" % (inet_ntoa(self.ip), self.mask_bits) def contains(self, other): """ Checks if an address or subnet is contained within the subnet other may be an InetSubnet or a SockAddrInet. """ if isinstance(other, InetSubnet): return (other.mask_bits >= self.mask_bits) and (packed_mask(other.addr_packed(), self.netmask_packed()) == self.addr_packed()) else: try: return ((other.ip & self.netmask_int()) == self.addr_int()) except: return False def getHostAddr(self, addr): """ Masks out the subnet part of addr. """ return addr.ip & ~self.netmask_int() def mapHostAddr(self, addr): """ Maps an address to the subnet. address is a 'host address', containing only the host specific part of the address, without the subnet part. This address will be mapped to this InetSubnet. """ return self.addr_int() | (addr & ~self.netmask_int()) def addr_packed(self): """ Function returning the address packed into a string of 4 characters. Uses the same representation as Python's socket.inet_pton. """ return self.ip def addr_str(self): """ Function returning the address as a string. Uses the same family specific representation as Python's socket.inet_ntop. """ return inet_ntop(socket.AF_INET, self.ip) def addr_int(self): """ Function returning the address as a 32-bit integer. """ return struct.unpack("I", self.ip)[0] def broadcast(self): """ """ if self.mask_bits == 0: return self.addr_int() return htonl(ntohl(self.addr_int()) | (0x7fffffff >> (self.mask_bits - 1))) def netmask_int(self): """ """ return struct.unpack("I", self.mask)[0] def netmask_bits(self): """ """ return self.mask_bits def netmask_packed(self): """ Function returning the netmask packed into a string of 4 characters. Uses the same representation as Python's socket.inet_pton. """ return self.mask def get_family(self): """ """ return socket.AF_INET class Inet6Subnet(Subnet): """ Class representing IPv6 address ranges. """ def __init__(self, addr): """ Constructor to initialize an InetSubnet instance A class representing Internet (IPv4) addresses, and IP segments. The address is represented in the XXX.XXX.XXX.XXX/M format, where XXX.XXX.XXX.XXX is the network address, and M specifies the number of ones (1) in the netmask. addr The string representation of an address, optionally with a subnet prefix length. """ def calculate_mask(bits): ret = "" while bits > 0: n = min(bits, 8) v = chr(((1 << n) - 1) << (8 - n)) ret += v bits = bits - n return ret.ljust(16, chr(0)) super(Inet6Subnet, self).__init__() parts = split(addr, '/') if len(parts) == 2: self.mask_bits = atoi(parts[1]) else: self.mask_bits = 128 self.mask = calculate_mask(self.mask_bits) self.ip = packed_mask(inet_pton(socket.AF_INET6, parts[0]), self.mask) def __str__(self): """ Function returning the string representation of this instance. """ return "%s/%u" % (inet_ntop(socket.AF_INET6, self.ip), self.mask_bits) def getHostAddr(self, addr): """ Masks out the subnet part of addr. """ return packed_mask(addr.pack(), packed_1operand(self.netmask_packed(), lambda x: ~x)) def mapHostAddr(self, addr): """ Maps an address to the subnet. address is a 'host address', containing only the host specific part of the address, without the subnet part. This address will be mapped to this Inet6Subnet. """ return struct.unpack("8H", packed_2operand(self.addr_packed(), packed_2operand(addr, packed_1operand(self.netmask_packed(), lambda x: ~x), lambda a, b: a & b), lambda a, b: a | b)) def addr_int(self): """ Function returning the address as a tuple of 8 16-bit integers. """ return struct.unpack("8H", self.ip) def addr_str(self): """ Function returning the address as a string. Uses the same family specific representation as Python's socket.inet_ntop. """ return inet_ntop(socket.AF_INET6, self.ip) def addr_packed(self): """ Function returning the address packed into a string of 16 characters. Uses the same representation as Python's socket.inet_pton. """ return self.ip def netmask_packed(self): """ Function returning the netmask packed into a string of 16 characters. Uses the same representation as Python's socket.inet_pton. """ return self.mask def netmask_bits(self): """ Return the subnet prefix length in bits. Equals to the number of 1s in the base-2 representation of the netmask. """ return self.mask_bits def netmask_int(self): """ Function returning the netmask as a tuple of 8 16-bit integers. """ return struct.unpack("8H", self.mask) def contains(self, other): """ Checks if an address or subnet is contained within the subnet other may be an Inet6Subnet or a SockAddrInet6. """ if isinstance(other, Inet6Subnet): return ((other.mask_bits >= self.mask_bits) & (packed_mask(other.ip, self.mask_bits) == self.ip)) else: try: return (packed_mask(other.pack(), self.netmask_packed()) == self.ip) except: return False def get_family(self): """ """ return socket.AF_INET6 class InetDomain(InetSubnet): """ """ deprecated_warning = True def __init__(self, addr): """ """ if (InetDomain.deprecated_warning): InetDomain.deprecated_warning = False log(None, CORE_DEBUG, 3, "Use of InetDomain class is deprecated, InetSubnet should be used instead.") super(InetDomain, self).__init__(addr) zorp-3.9.5/pylib/Zorp/Util.py000066400000000000000000000026561172670260400161000ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module containing utility functions used throughout Zorp. """ import collections def isSequence(value): if isinstance(value, basestring): return False return isinstance(value, collections.Sequence) def makeSequence(value): if not isSequence(value): return (value, ) return value zorp-3.9.5/pylib/Zorp/Zone.py000066400000000000000000000367761172670260400161100ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the Zones. This module defines the Zone class. Zones are the basis of access control in Zorp. A zone consists of a set of IP addresses or address ranges. For example, a zone can contain an IPv4 subnet. Zones are organized into a hierarchy created by the Zorp administrator. Children zones inherit the security attributes (set of permitted services etc.) from their parents. The administrative hierarchy often reflects the organization of the company, with zones assigned to the different departments. Zone definitions also determine which Zorp services can be started from the zone (outbound_services) and which services can enter the zone (inbound_services). When Zorp has to determine which zone a client belongs to, it selects the most specific zone containing the searched IP address. If an IP address belongs to two different zones, the straitest match is the most specific zone. Finding IP networks Suppose there are three zones configured: Zone_A containing the 10.0.0.0/8 network, Zone_B containing the 10.0.0.0/16 network, and Zone_C containing the 10.0.0.25 IP address. Searching for the 10.0.44.0 network returns Zone_B, because that is the most specific zone matching the searched IP address. Similarly, searching for 10.0.0.25 returns only Zone_C. This approach is used in the service definitions as well: when a client sends a connection request, Zorp looks for the most specific zone containing the IP address of the client. Suppose that the clients in Zone_A are allowed to use HTTP. If a client with IP 10.0.0.50 (thus belonging to Zone_B) can only use HTTP if Zone_B is the child of Zone_A, or if a service definition explicitly permits Zone_B to use HTTP. Zone examples The following example defines a simple zone hierarchy. The following zones are defined: internet: This zone contains every possible IP addresses, if an IP address does not belong to another zone, than it belongs to the internet zone. This zone accepts HTTP requests coming from the office zone, and can access the public HTTP and FTP services of the DMZ zone. office: This zone contains the 192.168.1.0/32 and 192.168.2.0/32 networks. The office zone can access the HTTP services of the internet zone, and use FTP to access the DMZ zone. External connections are not permitted to enter the zone (no inbound_services are defined). management: This zone is separated from the office zone, because it contans an independent subnet 192.168.3.0/32 . But from the Zorp administrator's view, it is the child zone of the office zone, meaning that it can use (and accept) the same services as the office zone: HTTP to the internet zone, and FTP to the DMZ zone. DMZ: This zone can accept connections HTTP and FTP connections from other zones, but cannot start external connections. Zone('internet', ['0.0.0.0/0', '::/0'], inbound_services=[ "office_http_inter"], outbound_services=[ "inter_http_dmz", "inter_ftp_dmz"]) Zone('office', ['192.168.1.0/32', '192.168.2.0/32'], outbound_services=[ "office_http_inter", "office_ftp_dmz"]) Zone('management', ['192.168.3.0/32'], admin_parent='office') Zone('DMZ', ['10.50.0.0/32'], inbound_services=[ "office_ftp_dmz", "inter_http_dmz", "inter_ftp_dmz"]) """ from Zorp import * from Subnet import Subnet, InetSubnet, Inet6Subnet from socket import htonl, ntohl from traceback import print_exc from Exceptions import ZoneException import types import radix import struct import kznf.kznfnetlink class Zone(object): """ Class encapsulating IP zones. This class encapsulates IPv4 and IPv6 zones; Determining the zone of an IP address An IP address always belongs to the most specific zone. Suppose that Zone A includes the IP network 10.0.0.0/8 and Zone B includes the network 10.0.1.0/24. In this case, a client machine with the 10.0.1.100/32 IP address belongs to both zones from an IP addressing point of view. But Zone B is more specific (in CIDR terms), so the client machine belongs to Zone B in Zorp. """ zone_subnet_tree = radix.Radix() zones = {} def __init__(self, name, addrs=(), inbound_services=None, outbound_services=None, admin_parent=None, umbrella=0): """ Constructor to initialize a Zone instance This constructor initializes a Zone object. name Name of the zone. addr A string representing an address range interpreted by the domain class (last argument), *or* a list of strings representing multiple address ranges. inbound_services A comma-separated list of services permitted to enter the zone. outbound_services A comma-separated list of services permitted to leave the zone. admin_parent Name of the administrative parent zone. If set, the current zone inherits the lists of permitted inbound and outbound services from its administrative parent zone. umbrella Enable this option for umbrella zones. Umbrella zones do not inherit the security attributes (list of permitted services) of their administrative parents. """ self.name = name self.admin_children = [] self.umbrella = umbrella self.inbound_services = set() self.outbound_services = set() if admin_parent is not None: self.admin_parent = self.zones[admin_parent] else: self.admin_parent = None self.zones[name] = self if isinstance(addrs, basestring): addrs = (addrs, ) self.subnets = map(Subnet.create, addrs) if inbound_services is not None: self.inbound_services = set(inbound_services) if outbound_services is not None: self.outbound_services = set(outbound_services) map(lambda i: log(None, CORE_DEBUG, 5, "Outbound service; zone='%s', service='%s'", (self.name, i)), self.outbound_services) map(lambda i: log(None, CORE_DEBUG, 5, "Inbound service; zone='%s', service='%s'", (self.name, i)), self.inbound_services) zone = reduce(lambda res, subnet: res or self.zone_subnet_tree.search_exact(packed=subnet.addr_packed()), self.subnets, None) if zone is not None: raise ZoneException, "Zone with duplicate IP range; zone=%s" % zone.data["zone"] for subnet in self.subnets: self.zone_subnet_tree.add(packed=subnet.addr_packed(), masklen=subnet.netmask_bits()).data["zone"] = self def __str__(self): """ """ return "Zone(%s)" % self.name def isInboundServicePermitted(self, service): """ """ if service.name in self.inbound_services or "*" in self.inbound_services: return ZV_ACCEPT elif self.admin_parent and not self.umbrella: return self.admin_parent.isInboundServicePermitted(service) return ZV_REJECT def isOutboundServicePermitted(self, service): """ """ if service.name in self.outbound_services or "*" in self.outbound_services: return ZV_ACCEPT elif self.admin_parent and not self.umbrella: return self.admin_parent.isOutboundServicePermitted(service) return ZV_REJECT def getName(self): """ """ return self.name def buildKZorpMessage(self): """ """ messages = [] flags = 0 if self.umbrella: flags = kznf.kznfnetlink.KZF_ZONE_UMBRELLA parent_name = None if self.admin_parent: parent_name = self.admin_parent.name # Zones with at most one subnet are serialized as is if len(self.subnets) == 0: messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_ZONE, kznf.kznfnetlink.create_add_zone_msg(self.name, flags, uname=self.name, pname=parent_name))) elif len(self.subnets) == 1: messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_ZONE, kznf.kznfnetlink.create_add_zone_msg(self.name, flags, self.subnets[0].get_family(), self.subnets[0].addr_packed(), self.subnets[0].netmask_packed(), uname=self.name, pname=parent_name))) else: # Zones with more than one subnet are exploded: first we send # the actual zone without any subnet addresses, then generate a # sub-zone for each subnet messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_ZONE, kznf.kznfnetlink.create_add_zone_msg(self.name, flags, uname=self.name, pname=parent_name))) # We send 'subzones' whose parents are the actual zones, and each # contains a subnet of the zone These 'subzones' do not have # umbrellas, so they inherit the DAC policy of the actual zone for index, subnet in enumerate(self.subnets): messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_ZONE, kznf.kznfnetlink.create_add_zone_msg(self.name, 0, subnet.get_family(), subnet.addr_packed(), subnet.netmask_packed(), uname="%s-#%u" % (self.name, index + 1), pname=self.name))) # Add DAC policy for i in self.inbound_services: messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_ZONE_SVC_IN, kznf.kznfnetlink.create_add_zone_svc_msg(self.name, i))) for i in self.outbound_services: messages.append((kznf.kznfnetlink.KZNL_MSG_ADD_ZONE_SVC_OUT, kznf.kznfnetlink.create_add_zone_svc_msg(self.name, i))) return messages @staticmethod def lookup(addr): """ """ if isinstance(addr, InetSubnet): addr_packed = addr.addr_packed()() elif isinstance(addr, Inet6Subnet): addr_packed = addr.addr_packed()() else: addr_packed = addr.pack() rnode = Zone.zone_subnet_tree.search_best(packed = addr_packed) if rnode: return rnode.data["zone"] else: return None @staticmethod def lookup_by_name(name): if name in Zone.zones: return Zone.zones[name] return None InetZone = Zone zorp-3.9.5/pylib/Zorp/Zorp.py000066400000000000000000000466131172670260400161160ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the Zorp core entry points. This module defines global constants (e.g., TRUE and FALSE) used by other Zorp components, and interface entry points to the Zorp core. Values returned by event handlers. ZV_UNSPEC ZV_ACCEPT ZV_DENY ZV_REJECT ZV_ABORT ZV_DROP ZV_POLICY The network protocol used in the server-side connection. ZD_PROTO_AUTO Use the protocol that is used on the client side. ZD_PROTO_TCP Use the TCP protocol on the server side. ZD_PROTO_UDP Use the UDP protocol on the server side. Options defining the source port of the server-side connection. Z_PORT_ANY Selected a random port between 1024 and 65535. This is the default behavior of every router. Z_PORT_GROUP Select a random port in the same group as the port used by the client. The following groups are defined: 0-513, 514-1024, 1025-. Z_PORT_EXACT Use the same port as the client. Z_PORT_RANDOM Select a random port using a cryptographically secure function. basic acl tags Z_BACL_REQUIRED Z_BACL_SUFFICIENT address families AF_UNSPEC AF_INET AF_INET6 Z_STACK_PROXY Z_STACK_PROGRAM Z_STACK_REMOTE Z_STACK_PROVIDER logical operators Z_NOT Z_AND Z_OR Z_XOR Z_EQ Z_NE Stacking options. Stack a proxy. Stack an external program. Stack a remote destination. Stack a Stacking Provider. global variables firewall_name"zorp" Core message tags CORE_SESSION"core.session" CORE_DEBUG"core.debug" CORE_ERROR"core.error" CORE_POLICY"core.policy" CORE_MESSAGE"core.message" CORE_AUTH"core.auth" Zorp exception types ZoneException"Zone not found" ServiceException"Service" DACException"DAC policy violation" MACException"MAC policy violation" AAException"Authentication or authorization failed" LimitException"Limit error" InternalException"Internal error occured" UserException"Incorrect, or unspecified parameter" LicenseException"Attempt to use unlicensed components" """ firewall_name = "zorp" # obsolete, not used anymore import traceback import sys import errno import socket import Config config = Config CORE_SESSION = "core.session" CORE_DEBUG = "core.debug" CORE_ERROR = "core.error" CORE_POLICY = "core.policy" CORE_MESSAGE = "core.message" CORE_AUTH = "core.auth" CORE_INFO = "core.info" # return values returned by event handlers ZV_UNSPEC = 0 ZV_ACCEPT = 1 ZV_DENY = 2 ZV_REJECT = 3 ZV_ABORT = 4 ZV_DROP = 5 ZV_POLICY = 6 ZV_ERROR = 7 # Legacy names Z_UNSPEC = ZV_UNSPEC Z_ACCEPT = ZV_ACCEPT Z_DENY = ZV_DENY Z_REJECT = ZV_REJECT Z_ABORT = ZV_ABORT Z_DROP = ZV_DROP Z_POLICY = ZV_POLICY Z_ERROR = ZV_ERROR # dispatched protocols ZD_PROTO_AUTO = 0 ZD_PROTO_TCP = 1 ZD_PROTO_UDP = 2 ZD_PROTO_NAME = ( "AUTO", # ZD_PROTO_AUTO "TCP", # ZD_PROTO_TCP "UDP", # ZD_PROTO_UDP ) # port allocation values Z_PORT_ANY = -1 Z_PORT_GROUP = -2 Z_PORT_EXACT = -3 Z_PORT_RANDOM = -4 # basic acl tags Z_BACL_REQUIRED = 1 Z_BACL_SUFFICIENT = 2 # stack types Z_STACK_PROXY = 1 Z_STACK_PROGRAM = 2 Z_STACK_REMOTE = 3 Z_STACK_PROVIDER = 4 Z_STACK_CUSTOM = 5 # proxy priorities Z_PROXY_PRI_LOW = 0 Z_PROXY_PRI_NORMAL = 1 Z_PROXY_PRI_HIGH = 2 Z_PROXY_PRI_URGENT = 3 # boolean values FALSE = 0 TRUE = 1 # address families AF_UNSPEC = socket.AF_UNSPEC AF_INET = socket.AF_INET AF_INET6 = socket.AF_INET6 # logical operators Z_NOT = "Z_NOT" Z_AND = "Z_AND" Z_OR = "Z_OR" Z_XOR = "Z_XOR" Z_EQ = "Z_EQ" Z_NE = "Z_XOR" Z_SZIG_TYPE_LONG = 1 Z_SZIG_TYPE_TIME = 2 Z_SZIG_TYPE_STRING = 3 Z_SZIG_TYPE_PROPS = 4 Z_SZIG_TYPE_CONNECTION_PROPS = 5 Z_SZIG_THREAD_START = 0 Z_SZIG_THREAD_STOP = 1 Z_SZIG_TICK = 2 Z_SZIG_COUNTED_IP = 3 Z_SZIG_CONNECTION_PROPS = 4 Z_SZIG_CONNECTION_STOP = 5 Z_SZIG_AUDIT_START = 6 Z_SZIG_AUDIT_STOP = 7 Z_SZIG_RELOAD = 8 Z_SZIG_AUTH_PENDING_BEGIN = 9 Z_SZIG_AUTH_PENDING_FINISH = 10 Z_SZIG_SERVICE_COUNT = 11 Z_SZIG_CONNECTION_START = 12 Z_KEEPALIVE_NONE = 0 Z_KEEPALIVE_CLIENT = 1 Z_KEEPALIVE_SERVER = 2 Z_KEEPALIVE_BOTH = 3 Z_SSL_VERIFY_NONE = 0 Z_SSL_VERIFY_OPTIONAL_UNTRUSTED = 1 Z_SSL_VERIFY_OPTIONAL_TRUSTED = 2 Z_SSL_VERIFY_REQUIRED_UNTRUSTED = 3 Z_SSL_VERIFY_REQUIRED_TRUSTED = 4 import Globals def init(names, virtual_name, is_master): """ Default init() function provided by Zorp This function is a default init() calling the init function identified by the name argument. This way several Zorp instances can use the same policy file. names Names (instance name and also-as names) of this instance. virtual_name string Virtual instance name of this process. If a Zorp instance is backed by multiple Zorp processes using the same configuration each process has a unique virtual instance name that is used for SZIG communication, PID file creation, etc. is_master int TRUE if Zorp is running in master mode, FALSE for slave processes. Each Zorp instance should have exactly one master process and an arbitrary number of slaves. """ import __main__ import SockAddr, KZorp, Rule import kznf.nfnetlink import kznf.kznfnetlink import errno # miscelanneous initialization if config.audit.encrypt_certificate_file: try: config.audit.encrypt_certificate = open(config.audit.encrypt_certificate_file, 'r').read() except IOError: log(None, CORE_ERROR, 1, "Error reading audit encryption certificate; file='%s'", (config.audit.encrypt_certificate_file)) if config.audit.encrypt_certificate_list_file: try: config.audit.encrypt_certificate_list = [ ] for list in config.audit.encrypt_certificate_list_file: newlist = [ ] for file in list: try: newlist.append( open(file, 'r').read() ) except IOError: log(None, CORE_ERROR, 1, "Error reading audit encryption certificate; file='%s'", (file)) config.audit.encrypt_certificate_list.append( newlist ) except TypeError: log(None, CORE_ERROR, 1, "Error iterating encryption certificate file list;") if config.audit.encrypt_certificate_list == None and config.audit.encrypt_certificate: config.audit.encrypt_certificate_list = [ [ config.audit.encrypt_certificate ] ] if config.audit.sign_private_key_file: try: config.audit.sign_private_key = open(config.audit.sign_private_key_file, 'r').read() except IOError: log(None, CORE_ERROR, 1, "Error reading audit signature's private key; file='%s'", (config.audit.sign_private_key_file)) if config.audit.sign_certificate_file: try: config.audit.sign_certificate = open(config.audit.sign_certificate_file, 'r').read() except IOError: log(None, CORE_ERROR, 1, "Error reading audit signature's certificate; file='%s'", (config.audit.sign_certificate_file)) Globals.rules = Rule.RuleSet() Globals.instance_name = names[0] for i in names: try: func = getattr(__main__, i) except AttributeError: ## LOG ## # This message indicates that the initialization function of # the given instance was not found in the policy file. ## log(None, CORE_ERROR, 0, "Instance definition not found in policy; instance='%s'", (names,)) return FALSE func() Globals.kzorp_responds_to_ping = False if config.options.kzorp_enabled: # ping kzorp to see if it's there try: h = KZorp.openHandle() m = h.create_message(kznf.nfnetlink.NFNL_SUBSYS_KZORP, kznf.kznfnetlink.KZNL_MSG_GET_ZONE, kznf.nfnetlink.NLM_F_REQUEST) m.set_nfmessage(kznf.kznfnetlink.create_get_zone_msg("__nonexistent_zone__")) result = h.talk(m, (0, 0), KZorp.netlinkmsg_handler) if result < 0 and result != -errno.ENOENT: log(None, CORE_ERROR, 0, "Error pinging KZorp, it is probably unavailable; result='%d'" % (result)) else: Globals.kzorp_responds_to_ping = True except: log(None, CORE_ERROR, 0, "Error pinging KZorp, it is probably unavailable; exc_value='%s'" % (sys.exc_value)) if Globals.kzorp_responds_to_ping: try: KZorp.downloadKZorpConfig(names[0], is_master) except: ## LOG ## # This message indicates that downloading the necessary information to the # kernel-level KZorp subsystem has failed. ## log(None, CORE_ERROR, 0, "Error downloading KZorp configuration, Python traceback follows; error='%s'" % (sys.exc_value)) for s in traceback.format_tb(sys.exc_traceback): for l in s.split("\n"): if l: log(None, CORE_ERROR, 0, "Traceback: %s" % (l)) # if kzorp did respond to the ping, the configuration is erroneous -- we die here so the user finds out return FALSE return TRUE def deinit(names, virtual_name): """ """ ## LOG ## # This message reports that the given instance is stopping. ## log(None, CORE_DEBUG, 6, "Deinitialization requested for instance; name='%s'", (names[0],)) for i in Globals.deinit_callbacks: i() def purge(): """ """ pass def cleanup(names, virtual_name, is_master): """ """ import KZorp ## LOG ## # This message reports that the given instance is freeing its external # resources (for example its kernel-level policy objects). ## log(None, CORE_DEBUG, 6, "Cleaning up instance; name='%s'", (names,)) if is_master and Globals.kzorp_responds_to_ping and config.options.kzorp_enabled: try: KZorp.flushKZorpConfig(names[0]) except: ## LOG ## # This message indicates that flushing the instance-related information in the # kernel-level KZorp subsystem has failed. ## log(None, CORE_ERROR, 0, "Error flushing KZorp configuration; error='%s'" % (sys.exc_value)) for s in traceback.format_tb(sys.exc_traceback): for l in s.split("\n"): if l: log(None, CORE_ERROR, 4, "Traceback: %s" % (l)) def notify(event, params): """ """ if Globals.notification_policy: return Globals.notification_policy.notify(event, params) ## NOLOG ## def log(sessionid, logclass, verbosity, msg, args=None): """ Function to send a message to the system log. This function can be used to send a message to the system log. sessionid The ID of the session the message belongs to. logclass Hierarchical log class as described in the zorp(8) manual page verbosity Verbosity level of the message. msg The message text. args Optional printf-style argument tuple added to the message. """ pass zorp-3.9.5/pylib/Zorp/__init__.py000066400000000000000000000021111172670260400167040ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, ## 2010, 2011 BalaBit IT Ltd, Budapest, Hungary ## ## 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. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ## ## ############################################################################ """ Module defining interface to the constructors """ zorp-3.9.5/pylib/kznf/000077500000000000000000000000001172670260400146165ustar00rootroot00000000000000zorp-3.9.5/pylib/kznf/Makefile.am000066400000000000000000000003661172670260400166570ustar00rootroot00000000000000EXTRA_DIST = setup.py SUBDIRS = kznf clean: $(PYTHON) $(top_srcdir)/pylib/kznf/setup.py clean --all distclean: clean install-exec-local: $(PYTHON) $(top_srcdir)/pylib/kznf/setup.py install --prefix $(DESTDIR)/$(prefix) --install-layout=deb zorp-3.9.5/pylib/kznf/kznf/000077500000000000000000000000001172670260400155665ustar00rootroot00000000000000zorp-3.9.5/pylib/kznf/kznf/Makefile.am000066400000000000000000000000651172670260400176230ustar00rootroot00000000000000EXTRA_DIST = __init__.py nfnetlink.py kznfnetlink.py zorp-3.9.5/pylib/kznf/kznf/__init__.py000066400000000000000000000000011172670260400176660ustar00rootroot00000000000000 zorp-3.9.5/pylib/kznf/kznf/kznfnetlink.py000066400000000000000000000620631172670260400205040ustar00rootroot00000000000000import struct import socket from nfnetlink import * import pprint # message types KZNL_MSG_START = 0 KZNL_MSG_COMMIT = 1 KZNL_MSG_FLUSH_ZONE = 2 KZNL_MSG_ADD_ZONE = 3 KZNL_MSG_ADD_ZONE_SVC_IN = 4 KZNL_MSG_ADD_ZONE_SVC_OUT = 5 KZNL_MSG_GET_ZONE = 6 KZNL_MSG_FLUSH_SERVICE = 7 KZNL_MSG_ADD_SERVICE = 8 KZNL_MSG_ADD_SERVICE_NAT_SRC = 9 KZNL_MSG_ADD_SERVICE_NAT_DST = 10 KZNL_MSG_GET_SERVICE = 11 KZNL_MSG_FLUSH_DISPATCHER = 12 KZNL_MSG_ADD_DISPATCHER = 13 KZNL_MSG_ADD_DISPATCHER_CSS = 14 KZNL_MSG_GET_DISPATCHER = 15 KZNL_MSG_COPY_ZONE_DAC = 16 KZNL_MSG_QUERY = 17 KZNL_MSG_ADD_RULE = 18 KZNL_MSG_ADD_RULE_ENTRY = 19 KZNL_MSG_ADD_BIND = 20 KZNL_MSG_GET_BIND = 21 KZNL_MSG_FLUSH_BIND = 22 KZNL_MSG_MAX = 22 # attribute types KZA_INVALID = 0 KZA_INSTANCE_NAME = 1 KZA_TR_PARAMS = 2 KZA_ZONE_PARAMS = 3 KZA_ZONE_NAME = 4 KZA_ZONE_UNAME = 5 KZA_ZONE_PNAME = 6 KZA_ZONE_RANGE = 7 KZA_SVC_PARAMS = 8 KZA_SVC_NAME = 9 KZA_SVC_ROUTER_DST_ADDR = 10 KZA_SVC_NAT_SRC = 11 KZA_SVC_NAT_DST = 12 KZA_SVC_NAT_MAP = 13 KZA_SVC_SESSION_CNT = 14 KZA_DPT_PARAMS = 15 KZA_DPT_NAME = 16 KZA_DPT_BIND_ADDR = 17 KZA_DPT_BIND_IFACE = 18 KZA_DPT_BIND_IFGROUP = 19 KZA_DPT_CSS_CZONE = 20 KZA_DPT_CSS_SZONE = 21 KZA_DPT_CSS_SERVICE = 22 KZA_QUERY_PARAMS = 23 KZA_QUERY_CLIENT_ZONE = 24 KZA_QUERY_SERVER_ZONE = 25 KZA_DISPATCHER_N_DIMENSION_PARAMS = 26 KZA_N_DIMENSION_RULE_ID = 27 KZA_N_DIMENSION_RULE_SERVICE = 28 KZA_N_DIMENSION_AUTH = 29 KZA_N_DIMENSION_IFACE = 30 KZA_N_DIMENSION_PROTO = 31 KZA_N_DIMENSION_SRC_PORT = 32 KZA_N_DIMENSION_DST_PORT = 33 KZA_N_DIMENSION_SRC_IP = 34 KZA_N_DIMENSION_SRC_ZONE = 35 KZA_N_DIMENSION_DST_IP = 36 KZA_N_DIMENSION_DST_ZONE = 37 KZA_N_DIMENSION_IFGROUP = 38 KZA_CONFIG_COOKIE = 39 KZA_INET4_ADDR= 40 KZA_INET4_SUBNET = 41 KZA_INET6_ADDR= 42 KZA_INET6_SUBNET = 43 KZA_N_DIMENSION_SRC_IP6 = 44 KZA_N_DIMENSION_DST_IP6 = 45 KZA_QUERY_PARAMS_SRC_IP = 46 KZA_QUERY_PARAMS_DST_IP = 47 KZA_SVC_ROUTER_DST_PORT = 48 KZA_BIND_ADDR = 49 KZA_BIND_PORT = 50 KZA_BIND_PROTO = 51 KZA_MAX = 51 # list of attributes in an N dimension rule N_DIMENSION_ATTRS = [ KZA_N_DIMENSION_AUTH, KZA_N_DIMENSION_IFACE, KZA_N_DIMENSION_PROTO, KZA_N_DIMENSION_SRC_PORT, KZA_N_DIMENSION_DST_PORT, KZA_N_DIMENSION_SRC_IP, KZA_N_DIMENSION_SRC_ZONE, KZA_N_DIMENSION_DST_IP, KZA_N_DIMENSION_DST_ZONE, KZA_N_DIMENSION_IFGROUP, KZA_N_DIMENSION_SRC_IP6, KZA_N_DIMENSION_DST_IP6, ] # name of global instance KZ_INSTANCE_GLOBAL = ".global" # transaction types KZ_TR_TYPE_INVALID = 0 KZ_TR_TYPE_ZONE = 1 KZ_TR_TYPE_SERVICE = 2 KZ_TR_TYPE_DISPATCHER = 3 # zone flags KZF_ZONE_UMBRELLA = 1 # service types KZ_SVC_INVALID = 0 KZ_SVC_PROXY = 1 KZ_SVC_FORWARD = 2 # service flags KZF_SVC_TRANSPARENT = 1 KZF_SVC_FORGE_ADDR = 2 # service NAT entry flags KZ_SVC_NAT_MAP_IPS = 1 KZ_SVC_NAT_MAP_PROTO_SPECIFIC = 2 # dispatcher types KZ_DPT_TYPE_INVALID = 0 KZ_DPT_TYPE_INET = 1 KZ_DPT_TYPE_IFACE = 2 KZ_DPT_TYPE_IFGROUP = 3 KZ_DPT_TYPE_N_DIMENSION = 4 # dispatcher flags KZF_DPT_TRANSPARENT = 1 KZF_DPT_FOLLOW_PARENT = 2 # dispatcher bind address port ranges KZF_DPT_PORT_RANGE_SIZE = 8 def get_family_from_attr(attr): if attr.type == KZA_INET4_ADDR or attr.type == KZA_INET4_SUBNET or \ attr.type == KZA_N_DIMENSION_SRC_IP or attr.type == KZA_N_DIMENSION_DST_IP: family = socket.AF_INET elif attr.type == KZA_INET6_ADDR or attr.type == KZA_INET6_SUBNET or \ attr.type == KZA_N_DIMENSION_SRC_IP6 or attr.type == KZA_N_DIMENSION_DST_IP6: family = socket.AF_INET6 else: raise ValueError, "attribute not supported; type='%d'" % attr.type return family ########################################################################### # helper functions to create/parse kzorp attributes ########################################################################### def create_name_attr(type, name): data = "".join((struct.pack('>H', len(name)), name)) return NfnetlinkAttribute(type, data) def parse_name_attr(attr): (len,) = struct.unpack('>H', attr.get_data()[:2]) (name,) = struct.unpack(str(len) + 's', attr.get_data()[2 : 2 + len]) return name def create_int8_attr(type, value): return NfnetlinkAttribute(type, struct.pack('B', value)) def parse_int8_attr(attr): (value,) = struct.unpack('B', attr.get_data()[0]) return value def create_int16_attr(type, value): return NfnetlinkAttribute(type, struct.pack('>H', value)) def parse_int16_attr(attr): (value,) = struct.unpack('>H', attr.get_data()[:2]) return value def create_int32_attr(type, value): return NfnetlinkAttribute(type, struct.pack('>I', value)) def parse_int32_attr(attr): (value,) = struct.unpack('>I', attr.get_data()[:4]) return value def create_int64_attr(type, value): return NfnetlinkAttribute(type, struct.pack('>Q', value)) def parse_int64_attr(attr): (value,) = struct.unpack('>Q', attr.get_data()[:8]) return value def create_inet_range_attr(type, family, address, mask): if family == socket.AF_INET: attr = NfnetlinkAttribute.create_inet_subnet_attr(KZA_INET4_SUBNET, family, address, mask) return NfnetlinkAttribute(type, attrs=[attr]) elif family == socket.AF_INET6: attr = NfnetlinkAttribute.create_inet_subnet_attr(KZA_INET6_SUBNET, family, address, mask) return NfnetlinkAttribute(type, attrs=[attr]) else: raise ValueError, "address family not supported; family='%d'" % family def parse_inet_range_attr(attr): attrs = attr.get_attributes() if len(attrs) == 0: raise ValueError, "zone range attribute does not contain a nested attribute" elif len(attrs) > 1: raise ValueError, "zone range attribute contains more than one nested attribute" attr = attrs[0] family = get_family_from_attr(attr) (addr, mask) = attr.parse_inet_subnet_attr(family) return (family, addr, mask) def create_inet_addr_attr(type, family, address): if family == socket.AF_INET: attr = NfnetlinkAttribute.create_inet_addr_attr(KZA_INET4_ADDR, family, address) return NfnetlinkAttribute(type, attrs=[attr]) elif family == socket.AF_INET6: attr = NfnetlinkAttribute.create_inet_addr_attr(KZA_INET6_ADDR, family, address) return NfnetlinkAttribute(type, attrs=[attr]) else: raise ValueError, "address family not supported; family='%d'" % family def parse_inet_addr_attr(attr): attrs = attr.get_attributes() if len(attrs) == 0: raise ValueError, "inet addr attribute does not contain a nested attribute" elif len(attrs) > 1: raise ValueError, "inet addr attribute contains more than one nested attribute" attr = attrs[0] family = get_family_from_attr(attr) addr = attr.parse_inet_addr_attr(family) return addr def create_port_range_attr(type, range_from, range_to): return NfnetlinkAttribute(type, struct.pack('>HH', range_from, range_to)) def parse_port_range_attr(attr): return struct.unpack('>HH', attr.get_data()[:4]) def create_nat_range_attr(type, flags, min_ip, max_ip, min_port, max_port): data = struct.pack('>IIIHH', flags, min_ip, max_ip, min_port, max_port) return NfnetlinkAttribute(type, data) def parse_nat_range_attr(attr): return struct.unpack('>IIIHH', attr.get_data()[:16]) def create_address_attr(type, proto, ip, port): return NfnetlinkAttribute(type, struct.pack('>IHB', ip, port, proto)) def parse_address_attr(attr): return struct.unpack('>IHB', attr.get_data()[:7]) def create_bind_addr_attr(type, proto, ip, ports): if len(ports) > KZF_DPT_PORT_RANGE_SIZE: raise ValueError, "bind address contains too many port ranges, %s allowed" % KZF_DPT_PORT_RANGE_SIZE data = struct.pack('>I', ip) for r in ports: data = "".join((data, struct.pack('>HH', r[0], r[1]))) if len(ports) < KZF_DPT_PORT_RANGE_SIZE: data = "".join((data, "\0" * 4 * (KZF_DPT_PORT_RANGE_SIZE - len(ports)))) data = "".join((data, struct.pack('BB', len(ports), proto))) return NfnetlinkAttribute(type, data) def parse_bind_addr_attr(attr): (addr,) = struct.unpack('>I', attr.get_data()[:4]) (num_ports, proto) = struct.unpack('BB', attr.get_data()[36:38]) ports = [] for i in range(num_ports): (start, end) = struct.unpack('>HH', attr.get_data()[4 + 4 * i : 8 + 4 * i]) ports.append((start, end)) return (proto, addr, ports) def create_bind_iface_attr(type, proto, iface, ports, pref_addr): if len(ports) > KZF_DPT_PORT_RANGE_SIZE: raise ValueError, "bind address contains too many port ranges, %s allowed" % KZF_DPT_PORT_RANGE_SIZE data = struct.pack('>I', pref_addr) for r in ports: data = "".join((data, struct.pack('>HH', r[0], r[1]))) if len(ports) < KZF_DPT_PORT_RANGE_SIZE: data = "".join((data, "\0" * 4 * (KZF_DPT_PORT_RANGE_SIZE - len(ports)))) data = "".join((data, struct.pack('BB', len(ports), proto), iface, "\0" * (16 - len(iface)))) return NfnetlinkAttribute(type, data) def parse_bind_iface_attr(attr): (pref_addr,) = struct.unpack('>I', attr.get_data()[:4]) (num_ports, proto) = struct.unpack('BB', attr.get_data()[36:38]) ports = [] for i in range(num_ports): (start, end) = struct.unpack('>HH', attr.get_data()[4 + 4 * i : 8 + 4 * i]) ports.append((start, end)) iface = attr.get_data()[38:].rstrip("\0") return (proto, iface, ports, pref_addr) def create_bind_ifgroup_attr(type, proto, group, mask, ports, pref_addr): if len(ports) > KZF_DPT_PORT_RANGE_SIZE: raise ValueError, "bind address contains too many port ranges, %s allowed" & KZF_DPT_PORT_RANGE_SIZE data = struct.pack('>III', group, mask, pref_addr) for r in ports: data = "".join((data, struct.pack('>HH', r[0], r[1]))) if len(ports) < KZF_DPT_PORT_RANGE_SIZE: data = "".join((data, "\0" * 4 * (KZF_DPT_PORT_RANGE_SIZE - len(ports)))) data = "".join((data, struct.pack('BB', len(ports), proto))) return NfnetlinkAttribute(type, data) def parse_bind_ifgroup_attr(attr): (group, mask, pref_addr) = struct.unpack('>III', attr.get_data()[:12]) (num_ports, proto) = struct.unpack('BB', attr.get_data()[44:46]) ports = [] for i in range(num_ports): (start, end) = struct.unpack('>HH', attr.get_data()[12 + 4 * i : 16 + 4 * i]) ports.append((start, end)) return (proto, group, mask, ports, pref_addr) def parse_n_dimension_attr(attr): (num_rules, ) = struct.unpack('>I', attr.get_data()[:4]) return num_rules def parse_rule_attrs(attr): (rule_id, ) = struct.unpack('>I', attr[KZA_N_DIMENSION_RULE_ID].get_data()[:4]) service = parse_name_attr(attr[KZA_N_DIMENSION_RULE_SERVICE]) rules = {} for dim_type in N_DIMENSION_ATTRS: if attr and attr.has_key(dim_type): data = attr[dim_type].get_data() value = struct.unpack('>I', data[:4]) rules[dim_type] = value return (rule_id, service, rules) def parse_rule_entry_attrs(attr): (rule_id, ) = struct.unpack('>I', attr[KZA_N_DIMENSION_RULE_ID].get_data()[:4]) rule_entries = {} for dim_type in N_DIMENSION_ATTRS: if attr and attr.has_key(dim_type): data = attr[dim_type].get_data() if dim_type == KZA_N_DIMENSION_AUTH or \ dim_type == KZA_N_DIMENSION_PROTO: value = struct.unpack('>B', data[:1]) elif dim_type == KZA_N_DIMENSION_DST_PORT or \ dim_type == KZA_N_DIMENSION_SRC_PORT: value = parse_port_range_attr(attr[dim_type]) elif dim_type == KZA_N_DIMENSION_DST_IP or \ dim_type == KZA_N_DIMENSION_SRC_IP: (addr, mask) = attr[dim_type].parse_inet_subnet_attr(get_family_from_attr(attr[dim_type])) value = (socket.AF_INET, addr, mask) elif dim_type == KZA_N_DIMENSION_DST_IP6 or \ dim_type == KZA_N_DIMENSION_SRC_IP6: (addr, mask) = attr[dim_type].parse_inet_subnet_attr(get_family_from_attr(attr[dim_type])) value = (socket.AF_INET6, addr, mask) elif dim_type == KZA_N_DIMENSION_IFGROUP: value =struct.unpack('>I', data[:4]) elif dim_type == KZA_N_DIMENSION_IFACE or \ dim_type == KZA_N_DIMENSION_DST_ZONE or \ dim_type == KZA_N_DIMENSION_SRC_ZONE: value = (parse_name_attr(attr[dim_type]), ) else: raise ValueError, "dispatcher dimension type is invalid; type='%d'" % dim_type rule_entries[dim_type] = value return (rule_id, rule_entries) def create_dispatcher_params_attr(type, dpt_type, dpt_flags, proxy_port): return NfnetlinkAttribute(type, struct.pack('>IHB', dpt_flags, proxy_port, dpt_type)) def parse_dispatcher_params_attr(attr): return struct.unpack('>IHB', attr.get_data()[:7]) def create_service_params_attr(type, svc_type, svc_flags): return NfnetlinkAttribute(type, struct.pack('>IB', svc_flags, svc_type)) def parse_service_params_attr(attr): return struct.unpack('>IB', attr.get_data()[:5]) def create_query_params_attr(type, proto, sport, dport, iface): data = struct.pack('>HH', sport, dport) data = "".join((data, iface, "\0" * (16 - len(iface)), struct.pack('>B', proto))) return NfnetlinkAttribute(type, data) ########################################################################### # helper functions to assemble kzorp messages ########################################################################### def create_start_msg(name, config_cookie=0): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_name_attr(KZA_INSTANCE_NAME, name)) if (config_cookie > 0): m.append_attribute(create_int64_attr(KZA_CONFIG_COOKIE, config_cookie)) return m def create_commit_msg(): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) return m def create_flush_msg(): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) return m # service def create_add_proxyservice_msg(name): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_service_params_attr(KZA_SVC_PARAMS, KZ_SVC_PROXY, 0)) m.append_attribute(create_name_attr(KZA_SVC_NAME, name)) return m def create_add_pfservice_msg(name, flags, dst_family = None, dst_ip = None, dst_port = None): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_service_params_attr(KZA_SVC_PARAMS, KZ_SVC_FORWARD, flags)) m.append_attribute(create_name_attr(KZA_SVC_NAME, name)) if dst_family and dst_ip and dst_port: m.append_attribute(create_inet_addr_attr(KZA_SVC_ROUTER_DST_ADDR, dst_family, dst_ip)) m.append_attribute(NfnetlinkAttributePort(KZA_SVC_ROUTER_DST_PORT, dst_port)) return m def create_add_service_nat_msg(name, mapping): # mapping is a tuple: (src, dst, map) # elements are tuples: (flags, min_ip, max_ip, min_port, max_port) m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_name_attr(KZA_SVC_NAME, name)) (src, dst, map) = mapping m.append_attribute(create_nat_range_attr(KZA_SVC_NAT_SRC, src[0], src[1], src[2], src[3], src[4])) if dst: m.append_attribute(create_nat_range_attr(KZA_SVC_NAT_DST, dst[0], dst[1], dst[2], dst[3], dst[4])) m.append_attribute(create_nat_range_attr(KZA_SVC_NAT_MAP, map[0], map[1], map[2], map[3], map[4])) return m def create_get_service_msg(name): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) if name: m.append_attribute(create_name_attr(KZA_SVC_NAME, name)) return m # zone def create_add_zone_msg(name, flags, family=socket.AF_INET, address = None, mask = None, uname = None, pname = None): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_int32_attr(KZA_ZONE_PARAMS, flags)) m.append_attribute(create_name_attr(KZA_ZONE_NAME, name)) if uname != None: m.append_attribute(create_name_attr(KZA_ZONE_UNAME, uname)) if pname != None: m.append_attribute(create_name_attr(KZA_ZONE_PNAME, pname)) if address != None and mask != None: m.append_attribute(create_inet_range_attr(KZA_ZONE_RANGE, family, address, mask)) return m def create_add_zone_svc_msg(name, service): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_name_attr(KZA_ZONE_UNAME, name)) m.append_attribute(create_name_attr(KZA_SVC_NAME, service)) return m def create_get_zone_msg(name): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) if name: m.append_attribute(create_name_attr(KZA_ZONE_UNAME, name)) return m # dispatcher def create_add_dispatcher_sabind_msg(name, flags, proto, proxy_port, rule_addr, rule_ports): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_dispatcher_params_attr(KZA_DPT_PARAMS, KZ_DPT_TYPE_INET, flags, proxy_port)) m.append_attribute(create_name_attr(KZA_DPT_NAME, name)) m.append_attribute(create_bind_addr_attr(KZA_DPT_BIND_ADDR, proto, rule_addr, rule_ports)) return m def create_add_dispatcher_ifacebind_msg(name, flags, proto, proxy_port, ifname, rule_ports, pref_addr = None): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_dispatcher_params_attr(KZA_DPT_PARAMS, KZ_DPT_TYPE_IFACE, flags, proxy_port)) m.append_attribute(create_name_attr(KZA_DPT_NAME, name)) if not pref_addr: pref_addr = 0 m.append_attribute(create_bind_iface_attr(KZA_DPT_BIND_IFACE, proto, ifname, rule_ports, pref_addr)) return m def create_add_dispatcher_ifgroupbind_msg(name, flags, proto, proxy_port, ifgroup, ifmask, rule_ports, pref_addr = None): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_dispatcher_params_attr(KZA_DPT_PARAMS, KZ_DPT_TYPE_IFGROUP, flags, proxy_port)) m.append_attribute(create_name_attr(KZA_DPT_NAME, name)) if not pref_addr: pref_addr = 0 m.append_attribute(create_bind_ifgroup_attr(KZA_DPT_BIND_IFGROUP, proto, ifgroup, ifmask, rule_ports, pref_addr)) return m def create_add_dispatcher_n_dimension(name, flags, proxy_port, num_rules): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_dispatcher_params_attr(KZA_DPT_PARAMS, KZ_DPT_TYPE_N_DIMENSION, flags, proxy_port)) m.append_attribute(create_name_attr(KZA_DPT_NAME, name)) m.append_attribute(create_int32_attr(KZA_DISPATCHER_N_DIMENSION_PARAMS, num_rules)) return m def create_add_n_dimension_rule_msg(dpt_name, rule_id, service, entry_nums): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_name_attr(KZA_DPT_NAME, dpt_name)) m.append_attribute(create_int32_attr(KZA_N_DIMENSION_RULE_ID, rule_id)) m.append_attribute(create_name_attr(KZA_N_DIMENSION_RULE_SERVICE, service)) for dim_type in N_DIMENSION_ATTRS: if entry_nums and entry_nums.has_key(dim_type): dim_size = entry_nums[dim_type] m.append_attribute(create_int32_attr(dim_type, dim_size)) return m def create_add_n_dimension_rule_entry_msg(dpt_name, rule_id, entry_values): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_name_attr(KZA_DPT_NAME, dpt_name)) m.append_attribute(create_int32_attr(KZA_N_DIMENSION_RULE_ID, rule_id)) for dim_type, value in entry_values.items(): if dim_type == KZA_N_DIMENSION_AUTH or \ dim_type == KZA_N_DIMENSION_PROTO: m.append_attribute(create_int8_attr(dim_type, value)) elif dim_type == KZA_N_DIMENSION_DST_PORT or \ dim_type == KZA_N_DIMENSION_SRC_PORT: m.append_attribute(create_port_range_attr(dim_type, value[0], value[1])) elif dim_type == KZA_N_DIMENSION_DST_IP or \ dim_type == KZA_N_DIMENSION_SRC_IP: m.append_attribute(NfnetlinkAttribute.create_inet_subnet_attr(dim_type, socket.AF_INET, value[0], value[1])) elif dim_type == KZA_N_DIMENSION_DST_IP6 or \ dim_type == KZA_N_DIMENSION_SRC_IP6: m.append_attribute(NfnetlinkAttribute.create_inet_subnet_attr(dim_type, socket.AF_INET6, value[0], value[1])) elif dim_type == KZA_N_DIMENSION_IFGROUP: m.append_attribute(create_int32_attr(dim_type, value)) elif dim_type == KZA_N_DIMENSION_IFACE or \ dim_type == KZA_N_DIMENSION_DST_ZONE or \ dim_type == KZA_N_DIMENSION_SRC_ZONE: m.append_attribute(create_name_attr(dim_type, value)) else: raise ValueError, "dispatcher dimension type is invalid; type='%d'" % dim_type return m def create_add_dispatcher_css_msg(name, service, czone = None, szone = None): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_name_attr(KZA_DPT_NAME, name)) if czone and czone != '*': m.append_attribute(create_name_attr(KZA_DPT_CSS_CZONE, czone)) if szone and szone != '*': m.append_attribute(create_name_attr(KZA_DPT_CSS_SZONE, szone)) m.append_attribute(create_name_attr(KZA_DPT_CSS_SERVICE, service)) return m def create_get_dispatcher_msg(name): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) if name: m.append_attribute(create_name_attr(KZA_DPT_NAME, name)) return m def create_query_msg(proto, family, saddr, sport, daddr, dport, iface): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) m.append_attribute(create_inet_addr_attr(KZA_QUERY_PARAMS_SRC_IP, family, saddr)) m.append_attribute(create_inet_addr_attr(KZA_QUERY_PARAMS_DST_IP, family, daddr)) m.append_attribute(create_query_params_attr(KZA_QUERY_PARAMS, proto, sport, dport, iface)) return m class NfnetlinkAttributePort(NfnetlinkAttribute): def __init__(self, type, port): NfnetlinkAttribute.__init__(self, type, struct.pack('>H', port)) def create_port_attr(type, port): return NfnetlinkAttributePort(type, port) def parse_port_attr(attr): return parse_int16_attr(attr) class NfnetlinkAttributeProto(NfnetlinkAttribute): def __init__(self, type, proto): if proto != socket.IPPROTO_TCP and proto != socket.IPPROTO_UDP: raise NfnetlinkAttributeException, "not supported protocol; proto='%d'" % proto NfnetlinkAttribute.__init__(self, type, struct.pack('>B', proto)) class NfnetlinkMessageAddBind(NfnetlinkMessage): def __init__(self, family, instance, addr, port, proto): NfnetlinkMessage.__init__(self, socket.AF_NETLINK, 0, 0) self.append_attribute(create_name_attr(KZA_INSTANCE_NAME, instance)) self.append_attribute(create_inet_addr_attr(KZA_BIND_ADDR, family, addr)) self.append_attribute(NfnetlinkAttributePort(KZA_BIND_PORT, port)) self.append_attribute(NfnetlinkAttributeProto(KZA_BIND_PROTO, proto)) class NfnetlinkMessageGetBind(NfnetlinkMessage): def __init__(self, instance = None): NfnetlinkMessage.__init__(self, socket.AF_NETLINK, 0, 0) if instance: self.append_attribute(create_name_attr(KZA_INSTANCE_NAME, instance)) @staticmethod def parse(msg): attrs = msg.get_nfmessage().get_attributes() for attr_type, attr in attrs.iteritems(): if attr_type == KZA_BIND_PROTO: proto = parse_int8_attr(attr) elif attr_type == KZA_BIND_PORT: port = parse_int16_attr(attr) elif attr_type == KZA_BIND_ADDR: addr_family = get_family_from_attr(attr.get_attributes()[0]) addr = parse_inet_addr_attr(attr) elif attr_type == KZA_INSTANCE_NAME: instance = parse_name_attr(attr) else: raise NfnetlinkAttributeException, "invalid attribute type in message, type='%d'" % attr_type if proto != socket.IPPROTO_TCP and proto != socket.IPPROTO_UDP: raise NfnetlinkAttributeException, "invalid attribute value of protocol, protocol='%d'" % proto return (instance, proto, addr_family, addr, port) def create_get_bind_msg(instance = None): return NfnetlinkMessageGetBind(instance) zorp-3.9.5/pylib/kznf/kznf/nfnetlink.py000066400000000000000000000367461172670260400201500ustar00rootroot00000000000000import socket import struct import ctypes class NfRootException(Exception): def __init__(self, detail): self.what = '' self.detail = detail def __str__(self): return '%s: %s' % (self.what, self.detail) class NfnetlinkException(NfRootException): def __init__(self, detail): super(NfnetlinkException, self).__init__(detail) self.what = 'nfnetlink error' class NfnetlinkAttributeException(NfRootException): def __init__(self, detail): super(NfnetlinkAttributeException, self).__init__(detail) self.what = 'nfnetlink attribute error' self.detail = detail class PacketException(NfRootException): def __init__(self, detail): super(PacketException, self).__init__(detail) self.what = 'packet parsing error' # netlink message type values NLM_F_REQUEST = 1 NLM_F_MULTI = 2 NLM_F_ACK = 4 NLM_F_ECHO = 8 # modifiers to GET request NLM_F_ROOT = 0x100 NLM_F_MATCH = 0x200 NLM_F_ATOMIC = 0x400 NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH # modifiers to NEW request NLM_F_REPLACE = 0x100 NLM_F_EXCL = 0x200 NLM_F_CREATE = 0x400 NLM_F_APPEND = 0x800 # netlink generic message types NLMSG_NOOP = 1 NLMSG_ERROR = 2 NLMSG_DONE = 3 NLMSG_OVERRUN = 4 # nfnetlink subsystems NFNL_SUBSYS_NONE = 0 NFNL_SUBSYS_CTNETLINK = 1 NFNL_SUBSYS_CTNETLINK_EXP = 2 NFNL_SUBSYS_QUEUE = 3 NFNL_SUBSYS_ULOG = 4 NFNL_SUBSYS_CTHELPER = 5 NFNL_SUBSYS_KZORP = 6 NETLINK_NETFILTER = 12 NLA_F_NESTED = (1 << 15) NLA_F_NET_BYTEORDER = (1 << 14) NLA_TYPE_MASK = ctypes.c_uint(~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)).value # attribute alignment NFA_ALIGNTO = 4 MAX_NLMSGSIZE = 65535 def nfa_align(len): return (len + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1) class NfnetlinkAttribute(object): def __init__(self, type, data = None, attrs = None): if data == None and attrs == None: raise NfnetlinkAttributeException, "either data or attr must be set" if data != None and attrs != None: raise NfnetlinkAttributeException, "only data or attr should be set" self.type = type self.nested = attrs != None if self.nested: self.type |= NLA_F_NESTED self.__buf = data self.__attrs = attrs def __eq__(self, other): if self.type != other.type: return False if self.nested != other.nested: return False if self.nested: res = self.__attrs == other.__attrs else: res = str(self.__buf) == str(other.__buf) return res def get_data(self): if self.nested == True: raise NfnetlinkAttributeException, "get data of nested attribute" return self.__buf def get_attributes(self): if self.nested == False: raise NfnetlinkAttributeException, "get nested attribute of normal attribute" return self.__attrs def dump(self): if self.nested == True: data = "" for attr in self.__attrs: data += attr.dump() else: data = self.__buf alen = nfa_align(len(data)) flen = alen - len(data) header = struct.pack('HH', alen + 4, self.type) data = "".join((header, data, '\0' * flen)) return data @staticmethod def __parse_impl(buf, index): attrs = {} while index < len(buf): header = buf[index:index + 4] if len(header) < 4: raise PacketException, "message too short to contain an attribute header" (length, type) = struct.unpack('HH', header) if length < 4: raise PacketException, "invalid attribute length specified in attribute header: too short to contain the header itself" data = buf[index + 4:index + length] if len(data) + 4 != length: raise PacketException, "message too short to contain an attribute of the specified size" nla_type = type & ctypes.c_uint(~NLA_TYPE_MASK).value type = type & NLA_TYPE_MASK if nla_type & NLA_F_NESTED: #pdb.set_trace() nested_attrs = NfnetlinkAttribute.__parse_impl(data, 0) attr = NfnetlinkAttribute(type, attrs=nested_attrs.values()) index = index + nfa_align(length) else: data = data.ljust(nfa_align(length), chr(0)) attr = NfnetlinkAttribute(type, data=data) index = index + nfa_align(length) if attrs.has_key(type): raise PacketException, "message contains multiple attributes of the same type" attrs[type] = attr return attrs @staticmethod def parse(buf): return NfnetlinkAttribute.__parse_impl(buf, 0) @staticmethod def create_inet_addr_attr(type, family, address): """Create an nfnetlink attribute which stores an IP address. Keyword arguments: addr -- an IP address in binary format (returned by inet_pton) """ if family != socket.AF_INET and family != socket.AF_INET6: raise NfnetlinkException, "protocol family not supported" if (family == socket.AF_INET): data = struct.pack('4s', address) else: data = struct.pack('16s', address) return NfnetlinkAttribute(type, data) def parse_inet_addr_attr(self, family): """Parse an nfnetlink attribute which stores an IP address. Return list of protocol family and address """ if (family != socket.AF_INET and family != socket.AF_INET6): raise NfnetlinkException, "protocol family not supported" if family == socket.AF_INET: data = struct.unpack('4s', self.__buf[0:4]) else: data = struct.unpack('16s', self.__buf[0:16]) return data[0] @staticmethod def create_inet_subnet_attr(type, family, address, mask): """Create an nfnetlink attribute which stores an IP subnet. Keyword arguments: addr -- an IP address in binary format (returned by inet_pton) mask -- an IP netmask in binary format (returned by inet_pton) """ if family != socket.AF_INET and family != socket.AF_INET6: raise NfnetlinkException, "protocol family not supported" #pdb.set_trace() if (family == socket.AF_INET): data = struct.pack('4s', address) + struct.pack('4s', mask) else: data = struct.pack('16s', address) + struct.pack('16s', mask) return NfnetlinkAttribute(type, data) def parse_inet_subnet_attr(self, family): """Parse an nfnetlink attribute which stores an IP subnet. Return list of protocol family, address and netmask """ if family != socket.AF_INET and family != socket.AF_INET6: raise NfnetlinkException, "protocol family not supported" if family == socket.AF_INET: data = struct.unpack('4s', self.__buf[0:4]) + struct.unpack('4s', self.__buf[4:8]) else: data = struct.unpack('16s', self.__buf[0:16]) + struct.unpack('16s', self.__buf[16:32]) return data class NfnetlinkMessage(object): def __init__(self, family, version, res_id, data="", parent = None): self.family = family self.version = version self.res_id = res_id self.__buf = data self.__attrs = None def __eq__(self, other): return self.get_attributes() == other.get_attributes() def get_attributes(self): return NfnetlinkAttribute.parse(self.__buf) def append_attribute(self, attribute): self.__buf = "".join((self.__buf, attribute.dump())) def dump(self): header = struct.pack('BBH', self.family, self.version, self.res_id) return "".join((header, self.__buf)) class NetlinkMessage(object): def __init__(self, type, flags, seq, pid, data): self.type = type self.flags = flags self.seq = seq self.pid = pid self.__buf = data def get_nfmessage(self): if len(self.__buf) < 4: raise PacketException, "message too short to contain an nfnetlink header" (family, version, res_id) = struct.unpack('BBH', self.__buf[:4]) return NfnetlinkMessage(family, version, res_id, self.__buf[4:], self) def get_errorcode(self): # the error message consists of an error code plus the header of the # message triggering the error if len(self.__buf) < (4 + 16): raise PacketException, "message too short to contain an error header" (error,) = struct.unpack('i', self.__buf[:4]) return error def set_nfmessage(self, nfmessage): self.child = nfmessage self.__buf = nfmessage.dump() def dump(self): if not self.child: raise PacketException, "cannot dump an incomplete netlink message" nfmsg = self.child.dump() # length of generic netlink message header is 16 bytes length = len(nfmsg) + 16 header = struct.pack('IHHII', length, self.type, self.flags, self.seq, self.pid) return "".join((header, nfmsg)) class PacketIn(object): def __init__(self, s): self.set_contents(s) def dump(self): return self.__buf def set_contents(self, s): self.__buf = s def get_messages(self): i = 0 messages = [] while i < len(self.__buf): header = self.__buf[i:i + 16] i = i + 16 if len(header) < 16: raise PacketException, "packet too short to contain a netlink message header" (length, type, flags, seq, pid) = struct.unpack('IHHII', header) if (length < 16): raise PacketException, "invalid length specified in netlink header: too short to contain a netlink message header" length = length - 16 data = self.__buf[i:i + length] i = i + length # length check if len(data) < length: raise PacketException, "packet too short to contain a message of the specified size" messages.append(NetlinkMessage(type, flags, seq, pid, data)) return messages class Subsystem(object): def __init__(self, id): self.id = id self.handle = None self.seq = 0 self.__callbacks = {} def next_seq(self): s = self.seq self.seq = self.seq + 1 return s def register_callback(self, type, callback): if not callable(callback): raise ValueError, "nfnetlink subsystem callback must be callable" self.__callbacks[type] = callback def unregister_callback(self, type): if self.__callbacks.has_key(type): del self.__callbacks[type] def dispatch(self, message): m_type = message.type & 255 if self.__callbacks.has_key(m_type): self.__callbacks[m_type](message) class Handle(object): def __init__(self): # subsystems self.__subsystems = {} # socket fd = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, NETLINK_NETFILTER) fd.bind((0, 0)) self.fd = fd # local address self.local = fd.getsockname() def close(self): self.fd.close() def register_subsystem(self, s): if self.__subsystems.has_key(s.id): raise NfnetlinkException, "subsystem already registered" self.__subsystems[s.id] = s s.handle = self def unregister_subsystem(self, s): if self.__subsystems.has_key(s.id): self.__subsystems[s.id].handle = None del self.__subsystems[s.id] else: raise NfnetlinkException, "subsystem has not been registered" def process_packet(self, packet): messages = packet.get_messages() for m in messages: self.dispatch(m) def dispatch(self, message): m_subsys = message.type >> 8 m_type = message.type & 0xff if self.__subsystems.has_key(m_subsys): self.__subsystems[m_subsys].dispatch(message) def create_message(self, subsys, type, flags = 0, data = ''): if not self.__subsystems.has_key(subsys): raise NfnetlinkException, "no such subsystem registered" s = self.__subsystems[subsys] return NetlinkMessage((subsys << 8) + type, flags, s.next_seq(), self.local[0], data) def send(self, message, to): self.fd.sendto(message.dump(), to) def listen(self, handler): quit = False status = 0 while not quit: (answer, peer) = self.fd.recvfrom(MAX_NLMSGSIZE) packet = PacketIn(answer) messages = packet.get_messages() for m in messages: # check for special messages if m.type == NLMSG_DONE: quit = True break if m.type == NLMSG_ERROR: quit = True status = m.get_errorcode() break # call handler if callable(handler): handler(m) return status def talk(self, message, to, handler): self.fd.sendto(message.dump(), to) return self.listen(handler) zorp-3.9.5/pylib/kznf/setup.py000066400000000000000000000005461172670260400163350ustar00rootroot00000000000000#!/bin/env python # -*- coding: utf-8 -*- from distutils.core import setup import sys, os srcdir = os.path.dirname(sys.path[0]) setup( package_dir = { 'kznf': os.path.join(srcdir, 'kznf/kznf') }, name="python-kzorp", description="Kzorp bindings for python", author="Krisztián Kovács", author_email="hidden@balabit.hu", packages=["kznf"] ) zorp-3.9.5/pylib/policy.boot000066400000000000000000000007321172670260400160340ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary ## All rights reserved. ## ## $Id: policy.boot,v 1.9 2003/05/30 15:40:15 bazsi Exp $ ## ############################################################################ # All modules referenced from C have to be imported here import Zorp.Zorp, Zorp.SockAddr, Zorp.Stream #Zorp.Satyr #import Zorp.Zorp #print "Policy bootstrapping..." zorp-3.9.5/scripts/000077500000000000000000000000001172670260400142165ustar00rootroot00000000000000zorp-3.9.5/scripts/Makefile.am000066400000000000000000000000621172670260400162500ustar00rootroot00000000000000sbin_SCRIPTS=kzorp EXTRA_DIST=kzorp genlutable.sh zorp-3.9.5/scripts/genlutable.sh000077500000000000000000000041461172670260400167040ustar00rootroot00000000000000#!/bin/bash # This file is processes input files to generate lookup tables for strings # -> ID mappings. All lines beginning with '#' or white space are ignored. # The first word contains a string, it will be used to generate a C # identifier which can then be used in the C code to easily identify a given # string. # # The identifier will be named as _ # The id is translated in the following way: # - characters are forced to upper case # - anything that cannot be present in a C identifier will be rewritten to '_' # usage() { echo "Syntax: $0 " echo "Options:" echo "Output type is: header|revtable|gperf" echo "Prefix is: the prefix to be used on C identifiers" } getline() { last_id=$[last_id + 1] read word dummy } gen_header() { echo -e "/* automatically generated by genlutable.sh, do not edit directly */" echo -e "#ifndef ${prefix}_NAMES_DEFINED\n#define ${prefix}_NAMES_DEFINED\n\n" echo -e "enum\n{\n" while getline; do cword=`echo $word | tr [a-z] [A-Z] | sed -r -e 's/"//g' -e 's/([]\.,[]|-)/_/g' -e 's/@/_AT_/' -e 's/_$//'` echo " ${prefix}_${cword}=$last_id," done echo -e " ${prefix}_MAX=$last_id\n" echo -e "};\n"; echo -e "#endif\n\n" } gen_revtable() { echo -e "/* automatically generated by genlutable.sh, do not edit directly */" array_name="`echo ${prefix} | tr [A-Z] [a-z]`_reverse_map " echo -e "const char *${array_name}[] = \n{\n" while getline; do cstr=$(echo "$word" | sed -r -e 's/"//g') echo -e " [${last_id}] = \"${cstr}\"," done echo -e "};\n"; } gen_gperf() { echo -e "/* automatically generated by genlutable.sh, do not edit directly */" echo -e "%readonly-tables\n%define initializer-suffix ,0" echo -e "struct tagid { char *name; int id; };\n\n%%" while getline; do echo "$word, $last_id" done } if [ $# -lt 3 ]; then usage exit 1 fi prefix=$2 last_id=0 word="" pattern='^["a-zA-Z0-9\-]' case "$1" in header) grep "$pattern" $3 | gen_header $prefix ;; revtable) grep "$pattern" $3 | gen_revtable $prefix ;; gperf) grep "$pattern" $3 | gen_gperf $prefix ;; esac zorp-3.9.5/scripts/kzorp000066400000000000000000000573131172670260400153170ustar00rootroot00000000000000#!/usr/bin/env python import os import optparse import sys import types import socket from kznf.kznfnetlink import * from kznf.nfnetlink import * KZNL_MSG_COPY_ZONE_DAC=16 AttributeRequiredError = "required attribute missing" def inet_ntoa(a): return "%s.%s.%s.%s" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff) def size_to_mask(family, size): if family == socket.AF_INET: max_size = 32 elif family == socket.AF_INET6: max_size = 128 else: raise ValueError, "address family not supported; family='%d'" % family if size > max_size: raise ValueError, "network size is greater than the maximal size; size='%d', max_size='%d'" % (size, max_size) packed_mask = '' actual_size = 0 while actual_size + 8 < size: packed_mask += '\xff' actual_size = actual_size + 8 if actual_size <= size: packed_mask += chr((0xff << (8 - (size - actual_size))) & 0xff) actual_size = actual_size + 8 while actual_size < max_size: packed_mask += '\x00' actual_size = actual_size + 8 return socket.inet_ntop(family, packed_mask) def mask_to_description(mask, definition): text = "" first = True for i in definition.keys(): if (mask & i): if first: text = definition[i] first = False else: text = text + ",%s" % (definition[i]) return text class DumpBase(): def __init__(self, quiet, type, create_func): self.quiet = quiet self.type = type self.create_func = create_func self.has_data = False def dump(self): # initialize nfnetlink h = Handle() h.register_subsystem(Subsystem(NFNL_SUBSYS_KZORP)) # create dump message m = h.create_message(NFNL_SUBSYS_KZORP, self.type, NLM_F_REQUEST | NLM_F_DUMP) m.set_nfmessage(self.create_func(None)) if not self.quiet: res = h.talk(m, (0, 0), self._msg_handler) else: res = h.talk(m, (0, 0), self._msg_handler_quiet) if res != 0: sys.stderr.write("Dump failed: result='%d' error='%s'\n" % (res, os.strerror(-res))) return 1 if self.quiet: if self.has_data: res = 0 else: res = 1 return res def _msg_handler(self, msg): pass def _msg_handler_quiet(self, msg): self.has_data = True class DumpZones(DumpBase): def __init__(self, quiet): DumpBase.__init__(self, quiet, KZNL_MSG_GET_ZONE, create_get_zone_msg) def _print_add_zone_svc(self, msg, attrs): if attrs.has_key(KZA_ZONE_UNAME): name = parse_name_attr(attrs[KZA_ZONE_UNAME]) else: raise AttributeRequiredError, "KZA_ZONE_UNAME" if attrs.has_key(KZA_SVC_NAME): svc_name = parse_name_attr(attrs[KZA_SVC_NAME]) else: raise AttributeRequiredError, "KZA_SVC_NAME" print "%s: '%s'" % (msg, svc_name) def _print_add_zone(self, attrs): zone_flags = {1: "umbrella"} if attrs.has_key(KZA_ZONE_PARAMS): flags = parse_int32_attr(attrs[KZA_ZONE_PARAMS]) else: raise AttributeRequiredError, "KZA_ZONE_PARAMS" if attrs.has_key(KZA_ZONE_NAME): name = parse_name_attr(attrs[KZA_ZONE_NAME]) else: name = None if attrs.has_key(KZA_ZONE_UNAME): unique_name = parse_name_attr(attrs[KZA_ZONE_UNAME]) else: raise AttributeRequiredError, "KZA_ZONE_UNAME" if attrs.has_key(KZA_ZONE_PNAME): admin_parent = parse_name_attr(attrs[KZA_ZONE_PNAME]) else: admin_parent = None if attrs.has_key(KZA_ZONE_RANGE): (family, addr, mask) = parse_inet_range_attr(attrs[KZA_ZONE_RANGE]) range_str = ", range '%s/%s'" % (socket.inet_ntop(family, addr), socket.inet_ntop(family, mask)) else: range_str = "" flags_str = mask_to_description(flags, zone_flags) print "Zone unique_name='%s', visible_name='%s', admin_parent='%s',\n flags '%s'%s" % \ (unique_name, name, admin_parent, flags_str, range_str) def _msg_handler(self, msg): attrs = msg.get_nfmessage().get_attributes() if msg.type & 0xff == KZNL_MSG_ADD_ZONE: self._print_add_zone(attrs) if msg.type & 0xff == KZNL_MSG_ADD_ZONE_SVC_IN: self._print_add_zone_svc(" Inbound service: ", attrs) if msg.type & 0xff == KZNL_MSG_ADD_ZONE_SVC_OUT: self._print_add_zone_svc(" Outbound service: ", attrs) class DumpServices(DumpBase): def __init__(self, quiet): DumpBase.__init__(self, quiet, KZNL_MSG_GET_SERVICE, create_get_service_msg) def _nat_range_str(self, flags, ip1, ip2, p1, p2): if ip1 == ip2: return "%s" % (inet_ntoa(ip1),) else: return "(%s - %s)" % (inet_ntoa(ip1), inet_ntoa(ip2)) def _print_add_svc_nat(self, msg, attrs): if attrs.has_key(KZA_SVC_NAME): name = parse_name_attr(attrs[KZA_SVC_NAME]) else: raise AttributeRequiredError, "KZA_SVC_NAME" if attrs.has_key(KZA_SVC_NAT_SRC): sflags, sip1, sip2, sp1, sp2 = parse_nat_range_attr(attrs[KZA_SVC_NAT_SRC]) else: raise AttributeRequiredError, "KZA_SVC_NAT_SRC" if attrs.has_key(KZA_SVC_NAT_DST): dflags, dip1, dip2, dp1, dp2 = parse_nat_range_attr(attrs[KZA_SVC_NAT_DST]) else: dflags = None if attrs.has_key(KZA_SVC_NAT_MAP): mflags, mip1, mip2, mp1, mp2 = parse_nat_range_attr(attrs[KZA_SVC_NAT_MAP]) else: raise AttributeRequiredError, "KZA_SVC_NAT_MAP" if dflags: print "%s src %s dst %s mapped to %s" % \ (msg, self._nat_range_str(sflags, sip1, sip2, sp1, sp2), \ self._nat_range_str(dflags, dip1, dip2, dp1, dp2), \ self._nat_range_str(mflags, mip1, mip2, mp1, mp2)) else: print "%s src %s mapped to %s" % \ (msg, self._nat_range_str(sflags, sip1, sip2, sp1, sp2), \ self._nat_range_str(mflags, mip1, mip2, mp1, mp2)) def _print_add_svc(self, attrs): svc_type = ("INVALID", "Service", "PFService") svc_flags = {1: "transparent", 2: "forge_addr"} if attrs.has_key(KZA_SVC_NAME): name = parse_name_attr(attrs[KZA_SVC_NAME]) else: raise AttributeRequiredError, "KZA_SVC_NAME" if attrs.has_key(KZA_SVC_PARAMS): flags, type = parse_service_params_attr(attrs[KZA_SVC_PARAMS]) else: raise AttributeRequiredError, "KZA_SVC_PARAMS" if attrs.has_key(KZA_SVC_SESSION_CNT): cnt = parse_int32_attr(attrs[KZA_SVC_SESSION_CNT]) else: cnt = None if attrs.has_key(KZA_SVC_ROUTER_DST_ADDR): addr = parse_inet_addr_attr(attrs[KZA_SVC_ROUTER_DST_ADDR]) else: addr = None if attrs.has_key(KZA_SVC_ROUTER_DST_PORT): port = parse_port_attr(attrs[KZA_SVC_ROUTER_DST_PORT]) else: port = None flags_str = mask_to_description(flags, svc_flags) print "Service name='%s', flags='%s', type='%s', session_cnt='%d'" % (name, flags_str, svc_type[type], cnt) if addr and port: print " router_dst='%s:%d'" % (socket.inet_ntoa(addr), port) def _msg_handler(self, msg): attrs = msg.get_nfmessage().get_attributes() if msg.type & 0xff == KZNL_MSG_ADD_SERVICE: self._print_add_svc(attrs) if msg.type & 0xff == KZNL_MSG_ADD_SERVICE_NAT_SRC: self._print_add_svc_nat(" SNAT: ", attrs) if msg.type & 0xff == KZNL_MSG_ADD_SERVICE_NAT_DST: self._print_add_svc_nat(" DNAT: ", attrs) class DumpDispatchers(DumpBase): def __init__(self, quiet): DumpBase.__init__(self, quiet, KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg) self.rules_value = {} self._max = 0 self._index = 0 self.dpt_protocols = {6: "TCP", 17: "UDP"} def _print_ports(self, ports): p = "" for s, e in ports: if s == e: p = "".join((p, "%d," % (s,))) else: p = "".join((p, "%d:%d," % (s, e))) return p.rstrip(",") def _print_add_dpt(self, attrs): dpt_flags = {1: "transparent", 2: "follow_parent"} if attrs.has_key(KZA_DPT_NAME): name = parse_name_attr(attrs[KZA_DPT_NAME]) else: raise AttributeRequiredError, "KZA_DPT_NAME" if attrs.has_key(KZA_DPT_PARAMS): flags, proxy_port, dpt_type = parse_dispatcher_params_attr(attrs[KZA_DPT_PARAMS]) else: raise AttributeRequiredError, "KZA_DPT_PARAMS" if dpt_type == KZ_DPT_TYPE_INET: if attrs.has_key(KZA_DPT_BIND_ADDR): proto, addr, ports = parse_bind_addr_attr(attrs[KZA_DPT_BIND_ADDR]) proto_str = self.dpt_protocols[proto] addr_str = " proto='%s', addr='%s', proxy_port='%d', num_ranges='%d', ports='%s'" % \ (proto_str, inet_ntoa(addr), proxy_port, len(ports), self._print_ports(ports)) else: raise AttributeRequiredError, "KZA_DPT_BIND_ADDR" elif dpt_type == KZ_DPT_TYPE_IFACE: if attrs.has_key(KZA_DPT_BIND_IFACE): proto, iface, ports, pref_addr = parse_bind_iface_attr(attrs[KZA_DPT_BIND_IFACE]) proto_str = self.dpt_protocols[proto] addr_str = " proto='%s', iface='%s', pref_addr='%s', proxy_port='%d', num_ranges='%d, ports='%s'" % \ (proto_str, iface, inet_ntoa(pref_addr), proxy_port, len(ports), self._print_ports(ports)) else: raise AttributeRequiredError, "KZA_DPT_BIND_IFACE" elif dpt_type == KZ_DPT_TYPE_IFGROUP: if attrs.has_key(KZA_DPT_BIND_IFGROUP): proto, group, mask, ports, pref_addr = parse_bind_ifgroup_attr(attrs[KZA_DPT_BIND_IFGROUP]) proto_str = self.dpt_protocols[proto] addr_str = " proto='%s', ifgroup='%s', pref_addr='%s', proxy_port='%d', num_ranges='%d', ports='%s'" % \ (proto_str, group, inet_ntoa(pref_addr), proxy_port, len(ports), self._print_ports(ports)) else: raise AttributeRequiredError, "KZA_DP_BIND_IFGROUP" elif dpt_type == KZ_DPT_TYPE_N_DIMENSION: num_rules = parse_n_dimension_attr(attrs[KZA_DISPATCHER_N_DIMENSION_PARAMS]) addr_str = " proxy_port='%d', num_rules='%d'" % (proxy_port, num_rules) flags_str = mask_to_description(flags, dpt_flags) print "Dispatcher name='%s' flags='%s'\n%s" % (name, flags_str, addr_str) def _print_add_dpt_css(self, attrs): if attrs.has_key(KZA_DPT_NAME): name = parse_name_attr(attrs[KZA_DPT_NAME]) else: raise AttributeRequiredError, "KZA_DPT_NAME" if attrs.has_key(KZA_DPT_CSS_CZONE): czone = parse_name_attr(attrs[KZA_DPT_CSS_CZONE]) else: czone = "*" if attrs.has_key(KZA_DPT_CSS_SZONE): szone = parse_name_attr(attrs[KZA_DPT_CSS_SZONE]) else: szone = "*" if attrs.has_key(KZA_SVC_NAME): sname = parse_name_attr(attrs[KZA_SVC_NAME]) else: raise AttributeRequiredError, "KZA_SVC_NAME" print " ('%s', '%s') -> '%s'" % (czone, szone, sname) def _print_add_rule(self, attrs): rule_id, service, rules = parse_rule_attrs(attrs) if (rules.values() == []): self._max = 0 else: (self._max,) = max(rules.values()) print " rule_id='%d', service='%s'" % (rule_id, service) def _print_add_rule_entry(self, attrs): dimensions = [ (KZA_N_DIMENSION_AUTH , 'auth'), (KZA_N_DIMENSION_IFACE , 'iface'), (KZA_N_DIMENSION_IFGROUP , 'ifgroup'), \ (KZA_N_DIMENSION_PROTO , 'proto'), (KZA_N_DIMENSION_SRC_PORT , 'src_port'), (KZA_N_DIMENSION_DST_PORT , 'dst_port'), \ (KZA_N_DIMENSION_SRC_IP , 'src_ip'), (KZA_N_DIMENSION_SRC_ZONE , 'src_zone'), (KZA_N_DIMENSION_DST_IP , 'dst_ip'), \ (KZA_N_DIMENSION_SRC_IP6 , 'src_ip'), (KZA_N_DIMENSION_DST_IP6 , 'dst_ip'), \ (KZA_N_DIMENSION_DST_ZONE , 'dst_zone') ] # NOTE: we detect that all entries were received by counting the # ADD_RULE_ENTRY messages and comparing that to the max # dimension array length. This is OK with the current kernel # implementation but may break if we change the kernel. rule_id, rule_entries = parse_rule_entry_attrs(attrs) for k, v in rule_entries.items(): if not k in self.rules_value: self.rules_value[k] = [] if k == KZA_N_DIMENSION_SRC_IP or k == KZA_N_DIMENSION_DST_IP or \ k == KZA_N_DIMENSION_SRC_IP6 or k == KZA_N_DIMENSION_DST_IP6: (family, addr, mask) = v self.rules_value[k].append((socket.inet_ntop(family, addr), socket.inet_ntop(family, mask))) elif k == KZA_N_DIMENSION_PROTO: self.rules_value[k].append(self.dpt_protocols[v[0]]) elif k == KZA_N_DIMENSION_SRC_PORT or k == KZA_N_DIMENSION_DST_PORT: self.rules_value[k].append((v[0], v[1])) else: self.rules_value[k].append(v[0]) self._index += 1 if self._index == self._max: for k in dimensions: if k[0] in self.rules_value: print " %s=%s " % (k[1], self.rules_value[k[0]]) self.rules_value = {} self._index = 0 print "" def _msg_handler(self, msg): attrs = msg.get_nfmessage().get_attributes() if msg.type & 0xff == KZNL_MSG_ADD_DISPATCHER: self._print_add_dpt(attrs) if msg.type & 0xff == KZNL_MSG_ADD_DISPATCHER_CSS: self._print_add_dpt_css(attrs) if msg.type & 0xff == KZNL_MSG_ADD_RULE: self._print_add_rule(attrs) if msg.type & 0xff == KZNL_MSG_ADD_RULE_ENTRY: self._print_add_rule_entry(attrs) class DumpBinds(DumpBase): def __init__(self, quiet): DumpBase.__init__(self, quiet, KZNL_MSG_GET_BIND, create_get_bind_msg) def _msg_handler(self, msg): (instance, proto, addr_family, addr, port) = NfnetlinkMessageGetBind.parse(msg) if proto == socket.IPPROTO_TCP: proto = "TCP" elif proto == socket.IPPROTO_UDP: proto = "UDP" print "Bind instance='%s' protocol='%s', address='%s', port='%d'" % (instance, proto, socket.inet_ntop(addr_family, addr), port) def upload_zones(fname): def exchange_message(h, msg, payload): m = h.create_message(NFNL_SUBSYS_KZORP, msg, NLM_F_REQUEST | NLM_F_ACK) m.set_nfmessage(payload) result = h.talk(m, (0, 0), None) if result != 0: raise NfnetlinkException, "Error while talking to KZorp, result='%d'" % result def parse_range(r): if r.count("/") == 0: # simple IP address addr = r mask = None else: # IP subnet (addr, mask) = r.split('/', 1) family = socket.AF_INET try: print family, addr socket.inet_pton(family, addr) except socket.error: family = socket.AF_INET6 socket.inet_pton(family, addr) if mask == None: if family == socket.AF_INET: mask = 32 elif family == socket.AF_INET6: mask = 128 mask = size_to_mask(family, int(mask)) return (socket.inet_pton(family, addr), socket.inet_pton(family, mask)) def process_line(h, l): # skip comments if l.startswith("#"): return zone, parent, umbrella, r = l.split(";") zone = zone.strip('"') parent = parent.strip('"') if parent == "": parent = None if int(umbrella) == 1: flags = KZF_ZONE_UMBRELLA else: flags = 0 ranges = r.split(",") if len(ranges) <= 1: if ranges == [""]: ranges = [] else: ranges = [r] # we send the "parent" first exchange_message(h, KZNL_MSG_ADD_ZONE, \ create_add_zone_msg(zone, flags, address=None, mask=None, uname=zone, pname=parent)) # then the rest for i in xrange(len(ranges)): uname = zone + "-#%d" % (i,) addr, mask = parse_range(ranges[i]) exchange_message(h, KZNL_MSG_ADD_ZONE, \ create_add_zone_msg(zone, flags, address=addr, mask=mask, uname=uname, pname=zone)) # initialize nfnetlink h = Handle() h.register_subsystem(Subsystem(NFNL_SUBSYS_KZORP)) # start zone transaction exchange_message(h, KZNL_MSG_START, create_start_msg(KZ_INSTANCE_GLOBAL)) # flush zones exchange_message(h, KZNL_MSG_FLUSH_ZONE, create_flush_msg()) exchange_message(h, KZNL_MSG_COPY_ZONE_DAC, create_flush_msg()) # process each zone f = file(fname) while 1: l = f.readline() if not l: break l = l.strip() try: process_line(h, l) except Exception, e: sys.stderr.write("Error while processing the following line: %s\n%s\n" % (e, l)) return 1 # commit transaction exchange_message(h, KZNL_MSG_COMMIT, create_commit_msg()) return 0 def evaluate(parser, args, quiet): def parse_ip(parser, ip, description): try: return (socket.AF_INET, socket.inet_pton(socket.AF_INET, ip)) except socket.error: try: return (socket.AF_INET, socket.inet_pton(socket.AF_INET6, ip)) except socket.error: parser.error("invalid %s ip: %s" % (description, ip)) def parse_port(parser, port, description): try: p = int(port) if (0 < p < 65535): return p else: raise ValueError, "" except ValueError: parser.error("invalid %s port: %s" % (description, port)) def handle_reply(r): attrs = r.get_nfmessage().get_attributes() client_zone = "not found" if attrs.has_key(KZA_QUERY_CLIENT_ZONE): client_zone = parse_name_attr(attrs[KZA_QUERY_CLIENT_ZONE]) server_zone = "not found" if attrs.has_key(KZA_QUERY_SERVER_ZONE): server_zone = parse_name_attr(attrs[KZA_QUERY_SERVER_ZONE]) service = "not found" if attrs.has_key(KZA_SVC_NAME): service = parse_name_attr(attrs[KZA_SVC_NAME]) dispatcher = "not found" if attrs.has_key(KZA_DPT_NAME): dispatcher = parse_name_attr(attrs[KZA_DPT_NAME]) print "Client zone: %s\nServer zone: %s\nService: %s\nDispatcher: %s" % \ (client_zone, server_zone, service, dispatcher) if args[0].lower() == "tcp": proto = socket.IPPROTO_TCP elif args[0].lower() == "udp": proto = socket.IPPROTO_UDP else: parser.error('protocol must be "tcp" or "udp"') (sfamily, saddr) = parse_ip(parser, args[1], "client") sport = parse_port(parser, args[2], "client") (dfamily, daddr) = parse_ip(parser, args[3], "server") dport = parse_port(parser, args[4], "server") if len(args[5]) > 16: parser.error('invalid interface name (>16 characters)') if sfamily != dfamily: parser.error('family of source and destination address is not the same') iface = args[5] if not quiet: print "evaluating %s:%s -> %s:%s on %s" % (args[1], sport, args[3], dport, iface) h = Handle() h.register_subsystem(Subsystem(NFNL_SUBSYS_KZORP)) kzorp_m = h.create_message(NFNL_SUBSYS_KZORP, KZNL_MSG_QUERY, NLM_F_REQUEST | NLM_F_ACK) m = create_query_msg(proto, sfamily, saddr, sport, daddr, dport, iface) kzorp_m.set_nfmessage(m) res = h.talk(kzorp_m, (0, 0), handle_reply) def main(args): option_list = [ optparse.make_option("-z", "--zones", action="store_true", dest="zones", default=False, help="dump KZorp zones " "[default: %default]"), optparse.make_option("-s", "--services", action="store_true", dest="services", default=False, help="dump KZorp services " "[default: %default]"), optparse.make_option("-d", "--dispatchers", action="store_true", dest="dispatchers", default=False, help="dump KZorp dispatchers " "[default: %default]"), optparse.make_option("-b", "--binds", action="store_true", dest="binds", default=False, help="dump KZorp instance bind parameters" "[default: %default]"), optparse.make_option("-e", "--evaluate", dest="evaluate", type="string", nargs=6, default=None, help="evaluate " "arguments: "), optparse.make_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="quiet operation " "[default: %default]"), optparse.make_option("-u", "--upload", action="store", type="string", dest="upload", default=None, help="upload KZorp zone structure from file " "[default: %default]") ] parser = optparse.OptionParser(option_list=option_list, prog="kzorp", usage = "usage: %prog [options]") (options, args) = parser.parse_args() if (options.zones or options.services or options.dispatchers or options.binds or options.upload != None or options.evaluate != None) == False: parser.error("at least one option must be set") if os.getuid() != 0: sys.stderr.write("kzorp must be run as root\n") return 2 res = 3 try: if options.zones: dump_zones = DumpZones(options.quiet) res = dump_zones.dump() if options.services: dump_services = DumpServices(options.quiet) res = dump_services.dump() if options.dispatchers: dump_dispatchers = DumpDispatchers(options.quiet) res = dump_dispatchers.dump() if options.binds: dump_binds = DumpBinds(options.quiet) res = dump_binds.dump() if options.upload: res = upload_zones(options.upload) if options.evaluate: res = evaluate(parser, options.evaluate, options.quiet) except socket.error, e: if e[0] == 111: sys.stderr.write("KZorp support not present in kernel\n") return 2 raise return res if __name__ == "__main__": res = main(sys.argv) sys.exit(res) zorp-3.9.5/tests/000077500000000000000000000000001172670260400136715ustar00rootroot00000000000000zorp-3.9.5/tests/Makefile.am000066400000000000000000000001231172670260400157210ustar00rootroot00000000000000 SUBDIRS = python unit kzorp tools EXTRA_DIST = functional/README.txt @TESTCASES@ zorp-3.9.5/tests/functional/000077500000000000000000000000001172670260400160335ustar00rootroot00000000000000zorp-3.9.5/tests/functional/README.txt000066400000000000000000000001431172670260400175270ustar00rootroot00000000000000 This directory contains the Testcase Database suitable for ZTS (Zorp Test System) for processing. zorp-3.9.5/tests/functional/anypy/000077500000000000000000000000001172670260400171735ustar00rootroot00000000000000zorp-3.9.5/tests/functional/anypy/info000066400000000000000000000000271172670260400200500ustar00rootroot00000000000000Base-Class: AnyPyProxy zorp-3.9.5/tests/functional/finger/000077500000000000000000000000001172670260400173055ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/attr/000077500000000000000000000000001172670260400202575ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/attr/cases/000077500000000000000000000000001172670260400213555ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/attr/cases/testcases000066400000000000000000000102021172670260400232710ustar00rootroot00000000000000 StartPolicy def config(self): FingerProxy.config(self) self.max_hop_count = 1 EndPolicy #strict_username_check = TRUE C2P: "&^%*(+-@barmi.com\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "barki@barhol.com@barhonnan.hu@barkie.org\r\n" P2S: "barki@barhol.com\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "barki@barhol.com\r\n" P2S: "barki@barhol.com\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) self.max_hostname_length = 5 self.max_hop_count = 10 EndPolicy C2P: "barki@123.56\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "barki@12.45\r\n" P2S: "barki@12.45\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) self.max_line_length = 12 self.max_hop_count = 10 EndPolicy C2P: "barki@12.45\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "barki@12.4\r\n" P2S: "barki@12.4\r\n" S2P: "barmilyen\r\n" P2C: "barmilyen\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) self.max_username_length = 5 self.max_hop_count = 10 EndPolicy C2P: "barkimas@alsomucsaj.hu\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "barki@alsomucsaj.hu\r\n" P2S: "barki@alsomucsaj.hu\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): self.max_hop_count = 10 def fingerRequest(self, username, hostname): if self.request_detailed == 1: self.request_detailed = 0 else: self.request_detailed = 1 if self.request_hostnames == '@mucsajalso.net': self.request_hostnames = '@mucsajfelso.org' if self.request_hostnames == '@mucsajalso.net@verpelet.org': self.request_hostnames = '@mucsajfelso.org@lachaza.net@verpelet.org' if self.request_username == 'barki': self.request_username = 'barkimas' return Z_ACCEPT EndPolicy C2P: "/W akarki@barhol.net\r\n" P2S: "akarki@barhol.net\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "akarki@barhol.net\r\n" P2S: "/W akarki@barhol.net\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "akarki@mucsajalso.net\r\n" P2S: "/W akarki@mucsajfelso.org\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "akarki@mucsajalso.net@verpelet.org\r\n" P2S: "/W akarki@mucsajfelso.org@lachaza.net@verpelet.org\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "barki@barhol.net\r\n" P2S: "/W barkimas@barhol.net\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "barki@mucsajalso.net\r\n" P2S: "/W barkimas@mucsajfelso.org\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) self.response_header = "header " self.response_footer = "footer" self.strict_username_check = FALSE self.max_hop_count = 10 EndPolicy C2P: "barki@12.45\r\n" P2S: "barki@12.45\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "header peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: "footer" P2C: Disconnect . C2P: "&^%*(+-@barmi.com\r\n" P2S: "&^%*(+-@barmi.com\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "header peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: "footer" P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) self.max_hop_count = 10 self.timeout = 6000 EndPolicy C2P: "barki@12.45\r\n" P2S: "barki@12.45\r\n" . C2P: "b" A= time.sleep(6) P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" .zorp-3.9.5/tests/functional/finger/attr/cases/timeout.tests000066400000000000000000000011641172670260400241310ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): FingerProxy.config(self) self.max_hop_count = 1 self.timeout = 3000 EndPolicy C2P: "barki@barhol.com@" A=time.sleep(6) C2P: "barhonnan.hu@barkie.org\r\n" #P2S: "barki@barhol.com\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" S2P: Disconnect P2C: Disconnect . C2P: "barki@barhol.com@barhonnan.hu@barkie.org\r\n" P2S: "barki@barhol.com\r\n" S2P: "peldaul " A=time.sleep(6) S2P: "barmilyen valasz\r\n" #P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/finger/attr/config000066400000000000000000000000001172670260400214350ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/func/000077500000000000000000000000001172670260400202405ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/func/cases/000077500000000000000000000000001172670260400213365ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/func/cases/testcases000066400000000000000000000127671172670260400232740ustar00rootroot00000000000000 StartPolicy def config(self): self.max_hop_count = 4 EndPolicy #Format: {C} / null command line C2P: "\r\n" P2S: "\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: "aztan meg sok barmilyen valasz\r\n" P2C: "aztan meg sok barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: " \r\n" P2S: "\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: "aztan meg sok barmilyen valasz\r\n" P2C: "aztan meg sok barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . #Format: {W}{C} C2P: "/W\r\n" P2S: "/W\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: "aztan meg sok barmilyen valasz\r\n" P2C: "aztan meg sok barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: " /W\r\n" P2S: "/W\r\n" . C2P: "/w\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . #Format: {W}{S}{U}{C} / name specified request C2P: "/W username\r\n" P2S: "/W username\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: "aztan meg sok barmilyen valasz\r\n" P2C: "aztan meg sok barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "&^%*(+-@hostname.org\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "/W username\r\n" P2S: "/W username\r\n" . C2P: "/W user@gepnev\r\n" P2S: "/W user@gepnev\r\n" . C2P: "/W user name\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "/Wusername\r\n" P2S: "/W username\r\n" . C2P: " /W username\r\n" P2S: "/W username\r\n" . C2P: "/w username\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . #Format: {H}{C} C2P: "@hostnev.org\r\n" P2S: "@hostnev.org\r\n" . C2P: "@192.168.131.46\r\n" P2S: "@192.168.131.46\r\n" . C2P: "@hostnev\r\n" P2S: "@hostnev\r\n" . C2P: " @hostnev.org\r\n" P2S: "@hostnev.org\r\n" . C2P: "hostnev.org\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . C2P: "@hostnev.org@hostnev.org\r\n" P2S: "@hostnev.org@hostnev.org\r\n" . C2P: "@hostnev.org@hostnev.org@hostnev.org\r\n" P2S: "@hostnev.org@hostnev.org@hostnev.org\r\n" . C2P: "@hostnev.org@hostnev.org alma korte\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . #Format: {W}{S}{H}{C} C2P: "/W @hostnev.org\r\n" P2S: "/W @hostnev.org\r\n" . C2P: "/W @hostnev\r\n" P2S: "/W @hostnev\r\n" . C2P: "/W@hostnev.org\r\n" P2S: "/W @hostnev.org\r\n" . C2P: "/W @hostnev.org@hostnev.org@hostnev.org\r\n" P2S: "/W @hostnev.org@hostnev.org@hostnev.org\r\n" . C2P: "/W@hostnev.org@hostnev.org@hostnev.org\r\n" P2S: "/W @hostnev.org@hostnev.org@hostnev.org\r\n" . C2P: "/W @hostnev.org@hostnev.org@hostnev.org alma korte\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . #Format:{U}{H}{C} C2P: "user@hostnev.org\r\n" P2S: "user@hostnev.org\r\n" . C2P: "user@hostnev.org@hostnev.org@hostnev.org\r\n" P2S: "user@hostnev.org@hostnev.org@hostnev.org\r\n" . C2P: "user@hostnev.org@hostnev.org@hostnev.org alma korte\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . C2P: "barmi user@hostnev.org@hostnev.org@hostnev.org\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" P2C: Disconnect . #Format:{W}{S}{U}{H}{C} C2P: "/W user@hostnev.hu\r\n" P2S: "/W user@hostnev.hu\r\n" . C2P: "/Wuser@hostnev.hu\r\n" P2S: "/W user@hostnev.hu\r\n" . C2P: "/W user@hostnev.org@hostnev.org@hostnev.org\r\n" P2S: "/W user@hostnev.org@hostnev.org@hostnev.org\r\n" . C2P: "/W user@hostnev.org@hostnev.org@hostnev.org alma korte\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . C2P: "/W barmi user@hostnev.org@hostnev.org@hostnev.org\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . C2P: "/Wuser@hostnev.org@hostnev.org@hostnev.org\r\n" P2S: "/W user@hostnev.org@hostnev.org@hostnev.org\r\n" . C2P: "/Wuser@hostnev.org@hostnev.org@hostnev.org alma korte\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . C2P: "/Wbarmi user@hostnev.org@hostnev.org@hostnev.org\r\n" P2C: "Finger protocol or disallowed protocol element, request denied.\r\n" . StartPolicy def config(self): FingerProxy.config(self) self.max_hop_count = 1 EndPolicy C2P: "user@hostname1.org@hostname2.org@hostname3.org\r\n" P2S: "user@hostname1.org\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . C2P: "user@hostname1.org\r\n" P2S: "user@hostname1.org\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) self.response_header = "header " self.response_footer = "footer" EndPolicy C2P: "user@hostname\r\n" P2S: "user\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "header peldaul barmilyen valasz\r\n" S2P: Disconnect P2C: "footer" P2C: Disconnect . StartPolicy def config(self): FingerProxy.config(self) EndPolicy #Long line in response C2P: "\r\n" P2S: "\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: "a"x1024 P2C: Disconnect . #Many lines in response C2P: "\r\n" P2S: "\r\n" S2P: "peldaul barmilyen valasz\r\n" P2C: "peldaul barmilyen valasz\r\n" S2P: "a\r\n"x50 P2C: "a\r\n"x50 S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/finger/func/config000066400000000000000000000000001172670260400214160ustar00rootroot00000000000000zorp-3.9.5/tests/functional/finger/info000066400000000000000000000000301172670260400201540ustar00rootroot00000000000000Base-Class: FingerProxy zorp-3.9.5/tests/functional/finger/spec000066400000000000000000000001201172670260400201530ustar00rootroot00000000000000StartSpec Proxy FingerProxy ZSleep 2 CSleep 2 EndSpec zorp-3.9.5/tests/functional/ftp/000077500000000000000000000000001172670260400166245ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/attr/000077500000000000000000000000001172670260400175765ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/attr/cases/000077500000000000000000000000001172670260400206745ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/attr/cases/testcases000066400000000000000000000263121172670260400226210ustar00rootroot00000000000000 # This testcase does not work, because it refers to self.client_data which # does not exist. #StartPolicy # def config(self): # self.permit_unknown_command = TRUE # self.request["PROBA"] = (FTP_REQ_POLICY, self.proba) # self.request["*"] = (FTP_REQ_ACCEPT) # self.response["*","*"] = (FTP_RSP_ACCEPT) # # because the proxy does not like invalid ports by default # # and the testsystem initiates connections from ports != 20 # self.strict_port_checking = FALSE # def proba(self,command): # (tmp,NULL) = split(str(self.client_data),':') # self.request_parameter = tmp[8:] # return FTP_REQ_ACCEPT #EndPolicy # #S2P: "220 kesz\r\n" #P2C: "220 kesz\r\n" #C2P: "USER user\r\n" #P2S: "USER user\r\n" #S2P: "230 logged in\r\n" #P2C: "230 logged in\r\n" # #CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") #C2P: Listen 1280 #C2P: "PORT " %CLIENT_COMMAIP ",5,0\r\n" #P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" #S2P: "200 \r\n" #P2C: "200 \r\n" #C2P: "RETR utvonal\r\n" #P2S: "RETR utvonal\r\n" #S2P: "150 \r\n" #P2C: "150 \r\n" #FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) #FW_PORT = PORT1*256 + PORT2 #S2P: Connect FW_IP FW_PORT #C2P: Accept #C2P: "PROBA\r\n" #P2S: "PROBA " %ZTS_CLIENT_IP "\r\n" #. # # This testcase does not work, because it refers to self.server_data which # does not exist. #StartPolicy # def config(self): # self.permit_unknown_command = TRUE # self.request["PROBA"] = (FTP_REQ_POLICY, self.proba) # self.response["*","*"] = (FTP_RSP_ACCEPT) # self.request["*"] = (FTP_REQ_ACCEPT) # self.strict_port_checking = FALSE # # def proba(self,command): # (tmp,NULL) = split(str(self.server_data),':') # self.request_parameter = tmp[8:] # return FTP_REQ_ACCEPT #EndPolicy # #S2P: "220 kesz\r\n" #P2C: "220 kesz\r\n" #C2P: "USER user\r\n" #P2S: "USER user\r\n" #S2P: "230 logged in\r\n" #P2C: "230 logged in\r\n" # #C2P: "PASV\r\n" #P2S: "PASV\r\n" #SERVER_COMMAIP = string.replace(ZTS_SERVER_IP,".",",") #S2P: Listen 1280 #S2P: "227 (" %SERVER_COMMAIP ",5,0)\r\n" #P2C: "227 (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" #FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) #FW_PORT = PORT1*256 + PORT2 #C2P: "RETR utvonal\r\n" #P2S: "RETR utvonal\r\n" #S2P: "150 \r\n" #P2C: "150 \r\n" #C2P: Connect FW_IP FW_PORT #S2P: Accept #C2P: "PROBA\r\n" #P2S: "PROBA " %ZTS_SERVER_IP "\r\n" #. StartPolicy def config(self): self.data_port_min = 40500 self.data_port_max = 40500 self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1280 C2P: "PORT " %CLIENT_COMMAIP ",5,0\r\n" #158*256+52=40500 P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 ",158,52\r\n" . #data mode #already tested in testcases.txt # This testcase does not work, because it refers to self.fw_server_data which # does not exist. #StartPolicy # def config(self): # self.permit_unknown_command = TRUE # self.request["PROBA"] = (FTP_REQ_POLICY, self.proba) # self.response["*","*"] = (FTP_RSP_ACCEPT) # self.request["*"] = (FTP_REQ_ACCEPT) # self.strict_port_checking = FALSE # # def proba(self,command): # (tmp,NULL) = split(str(self.fw_server_data),':') # self.request_parameter = tmp[8:] # return FTP_REQ_ACCEPT #EndPolicy # #S2P: "220 kesz\r\n" #P2C: "220 kesz\r\n" #C2P: "USER user\r\n" #P2S: "USER user\r\n" #S2P: "230 logged in\r\n" #P2C: "230 logged in\r\n" # #CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") #C2P: Listen 1280 #C2P: "PORT " %CLIENT_COMMAIP ",5,0\r\n" #P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" #S2P: "200 \r\n" #P2C: "200 \r\n" #C2P: "RETR utvonal\r\n" #P2S: "RETR utvonal\r\n" #S2P: "150 \r\n" #P2C: "150 \r\n" #FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) #FW_PORT = PORT1*256 + PORT2 #S2P: Connect FW_IP FW_PORT #C2P: Accept #C2P: "PROBA\r\n" #P2S: "PROBA " %FW_IP "\r\n" #. # This testcase does not work, because it refers to self.fw_client_data which # does not exist. #StartPolicy # def config(self): # self.permit_unknown_command = TRUE # self.request["PROBA"] = (FTP_REQ_POLICY, self.proba) # self.response["*","*"] = (FTP_RSP_ACCEPT) # self.request["*"] = (FTP_REQ_ACCEPT) # self.strict_port_checking = FALSE # # def proba(self,command): # (tmp,NULL) = split(str(self.fw_client_data),':') # self.request_parameter = tmp[8:] # return FTP_REQ_ACCEPT #EndPolicy # #S2P: "220 kesz\r\n" #P2C: "220 kesz\r\n" #C2P: "USER user\r\n" #P2S: "USER user\r\n" #S2P: "230 logged in\r\n" #P2C: "230 logged in\r\n" # #C2P: "PASV\r\n" #P2S: "PASV\r\n" #SERVER_COMMAIP = string.replace(ZTS_SERVER_IP,".",",") #S2P: Listen 1280 #S2P: "227 (" %SERVER_COMMAIP ",5,0)\r\n" #P2C: "227 (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" #FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) #FW_PORT = PORT1*256 + PORT2 #C2P: "RETR utvonal\r\n" #P2S: "RETR utvonal\r\n" #S2P: "150 \r\n" #P2C: "150 \r\n" #C2P: Connect FW_IP FW_PORT #S2P: Accept #C2P: "PROBA\r\n" #P2S: "PROBA " %FW_IP "\r\n" #. StartPolicy def config(self): self.max_line_length = 20 self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE EndPolicy S2P: "123 56789012345678901\r\n" P2S: Disconnect . S2P: "123 567890123456789\r\n" P2S: Disconnect . S2P: "123 56789012345678\r\n" P2C: "123 56789012345678\r\n" . StartPolicy def config(self): self.transparent_mode = FALSE self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.max_hostname_length = 10 self.strict_port_checking = FALSE EndPolicy P2C: "220-Welcome in Zorp FTP proxy authentication module!\r\n\ Please authenticate yourself!\r\n\ Authentication form:\r\n\ USER @\r\n\ PASS \r\n\ 220 \r\n" C2P: "USER ftp@123456.8.0\r\n" P2C: "331 Username and host okay, send your password.\r\n" . P2C: "220-Welcome in Zorp FTP proxy authentication module!\r\n\ Please authenticate yourself!\r\n\ Authentication form:\r\n\ USER @\r\n\ PASS \r\n\ 220 \r\n" C2P: "USER ftp@123456.8.01\r\n" P2C: "501 Hostname too long.\r\n" . StartPolicy def config(self): self.permit_unknown_command = FALSE self.max_username_length = 5 self.max_password_length = 5 self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.request["PROBA"] = (FTP_REQ_POLICY, self.proba) self.request["PROBA2"] = (FTP_REQ_POLICY, self.proba2) self.strict_port_checking = FALSE def proba(self,command): self.request_parameter = self.password return FTP_REQ_ACCEPT def proba2(self,command): self.request_parameter = self.username return FTP_REQ_ACCEPT EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER 12345\r\n" P2S: "USER 12345\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER 123456\r\n" P2C: "501 Username too long.\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER 12345\r\n" P2S: "USER 12345\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS 67890\r\n" P2S: "PASS 67890\r\n" S2P: "230 Access granted\r\n" P2C: "230 Access granted\r\n" C2P: "PROBA\r\n" P2S: "PROBA 67890\r\n" S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "PROBA2\r\n" P2S: "PROBA2 12345\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER 12345\r\n" P2S: "USER 12345\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS 123456\r\n" P2C: "501 Password too long.\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "\r\n" P2C: Disconnect P2S: Disconnect . StartPolicy def config(self): self.permit_empty_command = TRUE self.permit_unknown_command = TRUE self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "\r\n" C2P: "\r\n" C2P: "USER valami\r\n" P2S: "USER valami\r\n" . StartPolicy def config(self): self.permit_unknown_command = TRUE self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.request["USER"] = (FTP_REQ_POLICY, self.proba) self.strict_port_checking = FALSE def proba(self,command): if self.request_parameter == "valami": self.request_parameter = "barmi" else: self.request_parameter = "akarmi" return FTP_REQ_ACCEPT EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER valami\r\n" P2S: "USER barmi\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER proba\r\n" P2S: "USER akarmi\r\n" . StartPolicy def config(self): self.permit_unknown_command = TRUE self.response["*","*"] = (FTP_RSP_POLICY, self.proba) self.request["*"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE def proba(self,command,answer): if self.response_status == "345": self.response_status = "567" elif self.response_status == "346": self.response_status = "123" return FTP_RSP_ACCEPT EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER proba\r\n" P2S: "USER proba\r\n" S2P: "345 valami szoveg\r\n" P2C: "567 valami szoveg\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER proba\r\n" P2S: "USER proba\r\n" S2P: "346 valami szoveg\r\n" P2C: "123 valami szoveg\r\n" . StartPolicy def config(self): self.permit_unknown_command = TRUE self.response["*","*"] = (FTP_RSP_POLICY, self.proba) self.request["*"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE def proba(self,command,answer): if self.response_parameter == "proba": self.response_parameter = "nem proba" else: self.response_parameter = "megis proba" return FTP_RSP_ACCEPT EndPolicy S2P: "220 kesz\r\n" P2C: "220 megis proba\r\n" C2P: "USER proba\r\n" P2S: "USER proba\r\n" S2P: "345 proba\r\n" P2C: "345 nem proba\r\n" . S2P: "220 kesz\r\n" P2C: "220 megis proba\r\n" C2P: "USER proba\r\n" P2S: "USER proba\r\n" S2P: "346 valami szoveg\r\n" P2C: "346 megis proba\r\n" . StartPolicy def config(self): self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.response_strip_msg = TRUE self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 \r\n" C2P: "USER proba\r\n" P2S: "USER proba\r\n" S2P: "345 proba\r\n" P2C: "345 \r\n" . StartPolicy def config(self): self.transparent_mode = FALSE self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.target_port_range = str(server_port) self.strict_port_checking = FALSE EndPolicy P2C: "220-Welcome in Zorp FTP proxy authentication module!\r\n\ Please authenticate yourself!\r\n\ Authentication form:\r\n\ USER @\r\n\ PASS \r\n\ 220 \r\n" C2P: "USER ftp@" %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n" P2C: "331 Username and host okay, send your password.\r\n" C2P: "PASS jelszo\r\n" S2P: "220 Ok\r\n" P2S: "USER ftp\r\n" S2P: "331 send password\r\n" P2S: "PASS jelszo\r\n" S2P: "230 Access granted\r\n" P2C: "230 Access granted\r\n" . StartPolicy def config(self): self.timeout = 2000 self.response["*","*"] = (FTP_RSP_ACCEPT) self.request["*"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE EndPolicy S2P: "220 ready\r\n" P2C: "220 ready\r\n" A=time.sleep(3) P2C: "500 Connection timed out\r\n" P2S: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/ftp/attr/cases/timeout.tests000066400000000000000000000007101172670260400234440ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.timeout = 3000 self.strict_port_checking = FALSE EndPolicy S2P: "220" A=time.sleep(6) S2P: " Szia\r\n" #P2C: "220 Szia\r\n" P2C: Disconnect . S2P: "220 Szia\r\n" P2C: "220 Szia\r\n" C2P: "USER" A=time.sleep(6) C2P: " ftp\r\n" #P2S: "USER ftp\r\n" P2C: "500 Connection timed out\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/ftp/attr/config000066400000000000000000000000001172670260400207540ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/func/000077500000000000000000000000001172670260400175575ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/func/cases/000077500000000000000000000000001172670260400206555ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/func/cases/bug11316.tests000066400000000000000000000011061172670260400231100ustar00rootroot00000000000000StartGlobalInfo Tags bug11316 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220-first\r\n220-second\r\n221-third\r\n2200 fourth\r\n221-fifth\r\nsixth\r\n seventh\r\n220 eighth\r\n" #P2C: "220-first\r\n second\r\n" $x "220 eighth\r\n" P2C: "220-first\r\n second\r\n" " 221-third\r\n" " 2200 fourth\r\n" " 221-fifth\r\n" " sixth\r\n" " seventh\r\n" "220 eighth\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/bug11370.tests000066400000000000000000000015341172670260400231150ustar00rootroot00000000000000StartGlobalInfo Tags bug11370 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,21\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1301 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150-Data Follow\r\n150-Kakukk\r\n150 Vege\r\n" S2P: Connect IP PORT C2P: Accept dS2P: Disconnect P2C: "150-Data Follow\r\n Kakukk\r\n150 Vege\r\n" dP2C: Disconnect S2P: "226-Ok\r\n226 Vege\r\n" P2C: "226-Ok\r\n226 Vege\r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/bug12286.tests000066400000000000000000000005071172670260400231230ustar00rootroot00000000000000StartGlobalInfo Tags bug12286 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220-first\r\n220-second\r\n220 \r\n" P2C: "220-first\r\n second\r\n220 \r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/bug12342.tests000066400000000000000000000005311172670260400231110ustar00rootroot00000000000000StartGlobalInfo Tags bug12342 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "PORT\r\n" P2C: "500 Error parsing PORT parameters\r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/bug12941.tests000066400000000000000000000033261172670260400231230ustar00rootroot00000000000000StartGlobalInfo Tags bug12941 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy URG = chr(255)+chr(244)+chr(242) #FIXME: This is not the best behaviour. But for now it's enough to # not get abnormal program termination. S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,21\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1301 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150-Data Follow\r\n150-Kakukk\r\n150 Vege\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150-Data Follow\r\n Kakukk\r\n150 Vege\r\n" dP2C: "barmi\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "225 \r\n" P2C: "550 Data transfer failed\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,21\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1301 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150-Data Follow\r\n150-Kakukk\r\n150 Vege\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150-Data Follow\r\n Kakukk\r\n150 Vege\r\n" dP2C: "barmi\r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "225 \r\n" P2C: "225 \r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/bug13633.tests000066400000000000000000000043201172670260400231150ustar00rootroot00000000000000StartGlobalInfo Tags bug13633 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220 ProFTPD 1.3.1 Server (Debian)\r\n" P2C: "220 ProFTPD 1.3.1 Server (Debian)\r\n" C2P: "USER anonymous\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS legion@\r\n" P2S: "PASS legion@\r\n" S2P: "230 Anonymous access granted\r\n" P2C: "230 Anonymous access granted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" PORT=1290 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: Connect C_IP C_PORT C2P: "NLST\r\n" P2S: "NLST\r\n" S2P: "150 Opening ASCII mode data connection for file list\r\n" S2P: Accept dS2P: Disconnect P2C: "150 Opening ASCII mode data connection for file list\r\n" dP2C: Disconnect S2P: "226 Transfer complete.\r\n" P2C: "226 Transfer complete.\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 Goodbye.\r\n" P2C: "221 Goodbye.\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220 ProFTPD 1.3.0 Server (Debian)\r\n" P2C: "220 ProFTPD 1.3.0 Server (Debian)\r\n" C2P: "USER anonymous\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS legion@\r\n" P2S: "PASS legion@\r\n" S2P: "230 Anonymous access granted\r\n" P2C: "230 Anonymous access granted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" PORT=1290 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: Connect C_IP C_PORT C2P: "NLST\r\n" P2S: "NLST\r\n" S2P: Accept dS2P: Disconnect S2P: "226 Transfer complete.\r\n" dP2C: Disconnect P2C: "226 Transfer complete.\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 Goodbye.\r\n" P2C: "221 Goodbye.\r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/bug17571.tests000066400000000000000000000103011172670260400231160ustar00rootroot00000000000000StartGlobalInfo Tags bug17571 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE AbstractFtpProxy.loadAnswers(self) EndPolicy S2P: "220 --------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n" P2C: "220 --------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n" C2P: "USER someuser\r\n" P2S: "USER someuser\r\n" S2P: "331 User someuser OK. Password required\r\n" P2C: "331 User someuser OK. Password required\r\n" C2P: "PASS somepass\r\n" P2S: "PASS somepass\r\n" S2P: "230 Your bandwidth usage is restricted\r\n" P2C: "230 Your bandwidth usage is restricted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen 1290 S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "LIST\r\n" P2S: "LIST\r\n" C2P: Connect C_IP C_PORT S2P: Accept S2P: "150 Accepted data connection\r\n" dS2P: "barmi\r\n" dS2P: Disconnect P2C: "150 Accepted data connection\r\n" dP2C: "barmi\r\n" dP2C: Disconnect S2P: "226 Options: -l \r\n" P2C: "226 Options: -l \r\n" C2P: "TYPE I\r\n" P2S: "TYPE I\r\n" S2P: "200 TYPE is now 8-bit binary\r\n" P2C: "200 TYPE is now 8-bit binary\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: Connect C_IP C_PORT C2P: "TYPE A\r\n" P2S: "TYPE A\r\n" S2P: "200 TYPE is now ASCII\r\n" P2C: "200 TYPE is now ASCII\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "421 Logoff\r\n" P2C: "421 Logoff\r\n" P2C: Disconnect . S2P: "220 --------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n" P2C: "220 --------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n" C2P: "USER someuser\r\n" P2S: "USER someuser\r\n" S2P: "331 User someuser OK. Password required\r\n" P2C: "331 User someuser OK. Password required\r\n" C2P: "PASS somepass\r\n" P2S: "PASS somepass\r\n" S2P: "230 Your bandwidth usage is restricted\r\n" P2C: "230 Your bandwidth usage is restricted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen 1290 S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "LIST\r\n" P2S: "LIST\r\n" C2P: Connect C_IP C_PORT S2P: Accept S2P: "150 Accepted data connection\r\n" dS2P: "barmi\r\n" S2P: "226 Options: -l \r\n" P2C: "150 Accepted data connection\r\n" dS2P: "barmi2\r\n" dS2P: Disconnect dP2C: "barmi\r\nbarmi2\r\n" dP2C: Disconnect P2C: "226 Options: -l \r\n" C2P: "TYPE I\r\n" P2S: "TYPE I\r\n" S2P: "200 TYPE is now 8-bit binary\r\n" P2C: "200 TYPE is now 8-bit binary\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: Connect C_IP C_PORT C2P: "TYPE A\r\n" P2S: "TYPE A\r\n" S2P: "200 TYPE is now ASCII\r\n" P2C: "200 TYPE is now ASCII\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "421 Logoff\r\n" P2C: "421 Logoff\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/ftp/func/cases/bug17571b.tests000066400000000000000000000041421172670260400232660ustar00rootroot00000000000000StartGlobalInfo Tags bug17571 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE AbstractFtpProxy.loadAnswers(self) EndPolicy S2P: "220 --------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n" P2C: "220 --------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n" C2P: "USER someuser\r\n" P2S: "USER someuser\r\n" S2P: "331 User someuser OK. Password required\r\n" P2C: "331 User someuser OK. Password required\r\n" C2P: "PASS somepass\r\n" P2S: "PASS somepass\r\n" S2P: "230 Your bandwidth usage is restricted\r\n" P2C: "230 Your bandwidth usage is restricted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen 1290 S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "LIST\r\n" P2S: "LIST\r\n" C2P: Connect C_IP C_PORT S2P: Accept S2P: "150 Accepted data connection\r\n" dS2P: "barmi\r\n" S2P: "226 Options: -l \r\n" P2C: "150 Accepted data connection\r\n" dS2P: "barmi2\r\n" dS2P: Disconnect dP2C: "barmi\r\nbarmi2\r\n" dP2C: Disconnect P2C: "226 Options: -l \r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" C2P: "TYPE I\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: Connect C_IP C_PORT P2S: "TYPE I\r\n" C2P: "PASV\r\n" S2P: "200 TYPE is now 8-bit binary\r\n" P2C: "200 TYPE is now 8-bit binary\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "421 Logoff\r\n" P2C: "421 Logoff\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/ftp/func/cases/bug3043.tests000066400000000000000000000016411172670260400230320ustar00rootroot00000000000000StartGlobalInfo Tags bug3043 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.timeout = 3000 self.strict_port_checking = FALSE AbstractFtpProxy.loadAnswers(self) EndPolicy # Some valid scenarios S2P: "220 Szia\r\n" P2C: "220 Szia\r\n" C2P: "USER ftp\r\n" P2S: "USER ftp\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS myself@\r\n" P2S: "PASS myself@\r\n" S2P: "230 Anonymous access granted\r\n" P2C: "230 Anonymous access granted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "LIST\r\n" P2S: "LIST\r\n" A=time.sleep(10) P2C: "500 Connection timed out\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/ftp/func/cases/commands.tests000066400000000000000000000710561172670260400235530ustar00rootroot00000000000000#Access control commands StartPolicy def config(self): FtpProxyRW.config(self) self.request["SMNT"] = (FTP_REQ_ACCEPT) self.request["REIN"] = (FTP_REQ_ACCEPT) AbstractFtpProxy.loadAnswers(self) self.strict_port_checking = FALSE EndPolicy #USER S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "USER rossz_username\r\n" P2S: "USER rossz_username\r\n" S2P: "530 Not logged in\r\n" P2C: "530 Not logged in\r\n" C2P: "USER jo_username\r\n" P2S: "USER jo_username\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" . S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "USER remoteuser@remotehost\r\n" P2S: "USER remoteuser@remotehost\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "USER remoteuser@remotehost\r\n" P2C: "530 Already logged in.\r\n" . # # a '\' nem elfogadott karakter a usernevben. #S2P: "220 Service ready for new user\r\n" #P2C: "220 Service ready for new user\r\n" #C2P: "USER usern\ame\r\n" #P2S: "USER usern\ame\r\n" #S2P: "230 logged in\r\n" #P2C: "230 logged in\r\n" #. S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "USER ftp@212.40.96.74\r\n" P2S: "USER ftp@212.40.96.74\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" . #PASS S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "331 User name okay, need password\r\n" P2C: "331 User name okay, need password\r\n" C2P: "PASS rossz_password\r\n" P2S: "PASS rossz_password\r\n" S2P: "530 Not logged in\r\n" P2C: "530 Not logged in\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "332 Need account for login\r\n" P2C: "332 Need account for login\r\n" C2P: "PASS password\r\n" P2C: "503 Login with USER first.\r\n" . #ACCT S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "332 Need account for login\r\n" P2C: "332 Need account for login\r\n" C2P: "ACCT blurp\r\n" P2S: "ACCT blurp\r\n" S2P: "530 Not logged in\r\n" P2C: "530 Not logged in\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "ACCT blurp\r\n" P2C: "503 Login with USER first.\r\n" . #CWD S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 User logged in, proceed\r\n" P2C: "230 User logged in, proceed\r\n" C2P: "CwD utvonalacska\r\n" P2S: "CWD utvonalacska\r\n" S2P: "250 Requested file action okay, completed\r\n" P2C: "250 Requested file action okay, completed\r\n" C2P: "CWD utvona\lacska\r\n" P2S: "CWD utvona\lacska\r\n" S2P: "501 Syntax error in parameters or arguments\r\n" P2C: "501 Syntax error in parameters or arguments\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 User logged in, proceed\r\n" P2C: "230 User logged in, proceed\r\n" C2P: "CWD utvonalacska\r\n" P2S: "CWD utvonalacska\r\n" S2P: "502 Command not implemented\r\n" P2C: "502 Command not implemented\r\n" C2P: "CWD utvonalacska\r\n" P2S: "CWD utvonalacska\r\n" S2P: "530 Not logged in\r\n" P2C: "530 Not logged in\r\n" C2P: "CWD utvonalacska\r\n" P2S: "CWD utvonalacska\r\n" S2P: "550 Requested action not taken\r\n" P2C: "550 Requested action not taken\r\n" C2P: "CWD utvonalacska\r\n" P2S: "CWD utvonalacska\r\n" S2P: "260 barmi\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "CWD utvonalacska\r\n" P2S: "CWD utvonalacska\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #CDUP S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "CDUP valami\r\n" P2S: "CDUP\r\n" S2P: "200 ok\r\n" P2C: "200 ok\r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "500 error in command\r\n" P2C: "500 error in command\r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "550 \r\n" P2C: "550 \r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "260 barmi\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #SMNT / would mount the volume Stranger1 on Server as a guest S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "SMNT Stranger1:Server@*\r\n" P2S: "SMNT Stranger1:Server@*\r\n" S2P: "250 Requested file action okay, completed\r\n" P2C: "250 Requested file action okay, completed\r\n" . # would mount the volume Stranger2 on Server which is in the zone MyZone as "fred" with the password "fish" S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "SMNT Stranger2:Server@MyZone:fred:fish\r\n" P2S: "SMNT Stranger2:Server@MyZone:fred:fish\r\n" S2P: "250 Requested file action okay, completed\r\n" P2C: "250 Requested file action okay, completed\r\n" . #REIN S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "REIN\r\n" P2S: "REIN\r\n" S2P: "500 error in command\r\n" P2C: "500 error in command\r\n" C2P: "REiN\r\n" P2S: "REIN\r\n" S2P: "220 service ready\r\n" P2C: "220 service ready\r\n" C2P: "REIN\r\n" P2S: "REIN\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #QUIT S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 \r\n" P2C: "221 \r\n" P2C: Disconnect . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "500 \r\n" P2C: "500 \r\n" P2C: Disconnect . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "502 barmi\r\n" P2C: "500 " $NIHIL "\r\n" . #Transfer parameters #PORT S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "PORT 1,1,1,1,1,1\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "PORT 1,1,1,1,1\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "PORT 1,1,1,1,1,1\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "PORT 1,1,1,1,1,1\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "PORT 1,1,1,1,1,1\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" P2C: Disconnect . #PASV S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "227 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "227 valami\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "227 (1,1,1,1)\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "227 (1,1,1,1,1,1)\r\n" P2C: "227 (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "260 barmi\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #MODE S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "504 \r\n" P2C: "504 \r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "MODE s\r\n" P2S: "MODE S\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "MODE B\r\n" P2S: "MODE B\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "MODE C\r\n" P2S: "MODE C\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "260 barmi\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "MODE K\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "MODE S\r\n" P2S: "MODE S\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #TYPE S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "TYPE A\r\n" P2S: "TYPE A\r\n" S2P: "200 Representation type is ASCII\r\n" P2C: "200 Representation type is ASCII\r\n" C2P: "TYPE I\r\n" P2S: "TYPE I\r\n" S2P: "200 Representation type is IMAGE\r\n" P2C: "200 Representation type is IMAGE\r\n" C2P: "TYPE A N\r\n" P2S: "TYPE A\r\n" S2P: "200 Representation type is ASCII\r\n" P2C: "200 Representation type is ASCII\r\n" C2P: "TYPE E\r\n" P2C: "504 Command not implemented for that parameter\r\n" . #STRU S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "STRU F\r\n" P2S: "STRU F\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "STRU F\r\n" P2S: "STRU F\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "STRU F\r\n" P2S: "STRU F\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "STRU F\r\n" P2S: "STRU F\r\n" S2P: "504 \r\n" P2C: "504 \r\n" C2P: "STRU F\r\n" P2S: "STRU F\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "STRU R\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "STRU P\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "STRU barmi\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "STRU F\r\n" P2S: "STRU F\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #File action commands #ALLO size [ R max-record-size] StartInfo Tags kakukk EndInfo S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "ALLO 1 R 2\r\n" P2S: "ALLO 1 R 2\r\n" S2P: "200 Gyuhet\r\n" P2C: "200 Gyuhet\r\n" # Optional argument C2P: "ALLO 1\r\n" P2S: "ALLO 1\r\n" S2P: "200 Gyuhet\r\n" P2C: "200 Gyuhet\r\n" # Arguments must be positiv numbers C2P: "ALLO almafa R 2\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 1 R kortefa\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO cseresznye R almafa\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO ringlo\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO -1\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 1 R -2\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 999999999999999999999999999999999999999999999999999999\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 1 R 999999999999999999999999999999999999999999999999999999\r\n" P2C: "500 " $NIHIL "\r\n" # Missing argument C2P: "ALLO\r\n" P2C: "500 " $NIHIL "\r\n" # Bad separator C2P: "ALLO 1 S 3\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 1 RR 3\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 1 R 3\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ALLO 1 R 3\r\n" P2C: "500 " $NIHIL "\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "ALLO 123\r\n" P2S: "ALLO 123\r\n" S2P: "202 \r\n" P2C: "202 \r\n" C2P: "ALLO 123\r\n" P2S: "ALLO 123\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "ALLO 123\r\n" P2S: "ALLO 123\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "ALLO 123\r\n" P2S: "ALLO 123\r\n" S2P: "504 \r\n" P2C: "504 \r\n" C2P: "ALLO 123\r\n" P2S: "ALLO 123\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "ALLO 123\r\n" P2S: "ALLO 123\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy #REST S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "REST 1500\r\n" P2S: "REST 1500\r\n" S2P: "350 \r\n" P2C: "350 \r\n" C2P: "REST 0\r\n" P2S: "REST 0\r\n" S2P: "350 \r\n" P2C: "350 \r\n" # missing marker C2P: "REST\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "REST any_printable_characters\r\n" P2S: "REST any_printable_characters\r\n" S2P: "350 \r\n" P2C: "350 \r\n" . #STOR S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1280 C2P: "PORT " %CLIENT_COMMAIP ",5,0\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "REST 1500\r\n" P2S: "REST 1500\r\n" S2P: "350 \r\n" P2C: "350 \r\n" C2P: "STOR utvonal\r\n" P2S: "STOR utvonal\r\n" S2P: "150 \r\n" P2C: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "barmi\r\n" dP2C: Nothing . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1281 C2P: "PORT " %CLIENT_COMMAIP ",5,1\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "REST 1500\r\n" P2S: "REST 1500\r\n" S2P: "350 \r\n" P2C: "350 \r\n" C2P: "STOR utvonal\r\n" P2S: "STOR utvonal\r\n" S2P: "150 \r\n" P2C: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dC2P: "barmi\r\n" dP2S: "barmi\r\n" dC2P: Disconnect dP2S: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . #STOU S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1282 C2P: "PORT " %CLIENT_COMMAIP ",5,2\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "REST 1500\r\n" P2S: "REST 1500\r\n" S2P: "350 \r\n" P2C: "350 \r\n" C2P: "STOU\r\n" P2S: "STOU\r\n" S2P: "150 \r\n" P2C: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "barmi\r\n" dP2C: Nothing . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1283 C2P: "PORT " %CLIENT_COMMAIP ",5,3\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "REST 1500\r\n" P2S: "REST 1500\r\n" S2P: "350 \r\n" P2C: "350 \r\n" C2P: "STOU\r\n" P2S: "STOU\r\n" S2P: "150 \r\n" P2C: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dC2P: "barmi\r\n" dP2S: "barmi\r\n" dC2P: Disconnect dP2S: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . #RETR S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1284 C2P: "PORT " %CLIENT_COMMAIP ",5,4\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "RETR utvonal\r\n" P2S: "RETR utvonal\r\n" S2P: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 \r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . #LIST S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1285 C2P: "PORT " %CLIENT_COMMAIP ",5,5\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "LIST utvonal\r\n" P2S: "LIST utvonal\r\n" S2P: "500 \r\n" P2C: "500 \r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1285 C2P: "PORT " %CLIENT_COMMAIP ",5,5\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "LIST utvonal\r\n" P2S: "LIST utvonal\r\n" S2P: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 \r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1286 C2P: "PORT " %CLIENT_COMMAIP ",5,6\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "LIST utvonal\r\n" P2S: "LIST utvonal\r\n" S2P: "450 \r\n" P2C: "450 \r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1286 C2P: "PORT " %CLIENT_COMMAIP ",5,6\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "LIST utvonal\r\n" P2S: "LIST utvonal\r\n" S2P: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 \r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . #NLST S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1287 C2P: "PORT " %CLIENT_COMMAIP ",5,7\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "NLST utvonal\r\n" P2S: "NLST utvonal\r\n" S2P: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 \r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . #APPE S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1288 C2P: "PORT " %CLIENT_COMMAIP ",5,8\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "APPE utvonal\r\n" P2S: "APPE utvonal\r\n" S2P: "150 \r\n" P2C: "150 \r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dC2P: "barmi\r\n" dP2S: "barmi\r\n" dC2P: Disconnect dP2S: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . #RNFR / RNTO S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "RNFR barmirol\r\n" P2S: "RNFR barmirol\r\n" S2P: "350 szoveg szoveg\r\n" P2C: "350 szoveg szoveg\r\n" C2P: "RNTO barmire\r\n" P2S: "RNTO barmire\r\n" S2P: "250 valami barmi\r\n" P2C: "250 valami barmi\r\n" C2P: "RNFR barmirol\r\n" P2S: "RNFR barmirol\r\n" S2P: "100 szoveg szoveg\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "RNFR barmirol\r\n" P2S: "RNFR barmirol\r\n" S2P: "350 szoveg szoveg\r\n" P2C: "350 szoveg szoveg\r\n" C2P: "RNTO barmire\r\n" P2S: "RNTO barmire\r\n" S2P: "150 valami barmi\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "RNFR barmirol\r\n" P2S: "RNFR barmirol\r\n" S2P: "350 szoveg szoveg\r\n" P2C: "350 szoveg szoveg\r\n" C2P: "RNTO barmire\r\n" P2S: "RNTO barmire\r\n" S2P: "502 valami barmi\r\n" P2C: "502 valami barmi\r\n" . #missing or invalid RNFR S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "RNFR barmirol\r\n" P2S: "RNFR barmirol\r\n" S2P: "550 szoveg szoveg\r\n" P2C: "550 szoveg szoveg\r\n" C2P: "RNTO barmire\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "RNFR barmirol\r\n" P2S: "RNFR barmirol\r\n" S2P: "550 szoveg szoveg\r\n" P2C: "550 szoveg szoveg\r\n" C2P: "RNTO barmire\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "RNTO barmire\r\n" P2C: "500 " $NIHIL "\r\n" . #DELE S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "250 ok\r\n" P2C: "250 ok\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "450 not ok\r\n" P2C: "450 not ok\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "550 not ok\r\n" P2C: "550 not ok\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "500 se\r\n" P2C: "500 se\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "501 se\r\n" P2C: "501 se\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "502 cni\r\n" P2C: "502 cni\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "530 nli\r\n" P2C: "530 nli\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "563 nli\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "DELE utvonalacska\r\n" P2S: "DELE utvonalacska\r\n" S2P: "421 sd\r\n" P2C: "421 Logoff\r\n" P2C: Disconnect . #RMD S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "250 \r\n" P2C: "250 \r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "550 \r\n" P2C: "550 \r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "551 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "RMD barmi\r\n" P2S: "RMD barmi\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #MKD S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "257 \r\n" P2C: "257 \r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "550 \r\n" P2C: "550 \r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "551 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "MKD barmi\r\n" P2S: "MKD barmi\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #PWD S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "257 \r\n" P2C: "257 \r\n" C2P: "PWD\r\n" P2S: "PWD\r\n" S2P: "257 \r\n" P2C: "257 \r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "550 \r\n" P2C: "550 \r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "551 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "PWD barmi\r\n" P2S: "PWD\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . URG = chr(255)+chr(244)+chr(242) #ABOR S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "225 \r\n" P2C: "225 \r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "226 \r\n" P2C: "226 \r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "227 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "ABOR\r\n" P2S: %URG "ABOR\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" P2C: Disconnect . #Informational commands #SYST S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "SYST\r\n" P2S: "SYST\r\n" S2P: "215 \r\n" P2C: "215 \r\n" C2P: "SYST\r\n" P2S: "SYST\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "SYST\r\n" P2S: "SYST\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "SYST\r\n" P2S: "SYST\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "SYST\r\n" P2S: "SYST\r\n" S2P: "216 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "SYST\r\n" P2S: "SYST\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #STAT S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "211 \r\n" P2C: "211 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "212 \r\n" P2C: "212 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "213 \r\n" P2C: "213 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "450 \r\n" P2C: "450 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "530 \r\n" P2C: "530 \r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "216 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.request["HELP"] = (FTP_REQ_ACCEPT) self.request["SITE"] = (FTP_REQ_ACCEPT) self.request["EPRT"] = (FTP_REQ_ACCEPT) self.request["EPSV"] = (FTP_REQ_ACCEPT) AbstractFtpProxy.loadAnswers(self) self.strict_port_checking = FALSE EndPolicy #HELP and multi-line help reply S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "HELP\r\n" P2S: "HELP\r\n" S2P: "214-First line\r\n\ Second line\r\n\ 234 A line beginning with numbers\r\n\ 214 The last line\r\n" P2C: "214-First line\r\n\ Second line\r\n\ 234 A line beginning with numbers\r\n\ 214 The last line\r\n" C2P: "HELP USER\r\n" P2S: "HELP USER\r\n" S2P: "214 \r\n" P2C: "214 \r\n" C2P: "HELP user\r\n" P2S: "HELP user\r\n" S2P: "214 \r\n" P2C: "214 \r\n" C2P: "HELP barmi\r\n" P2S: "HELP barmi\r\n" S2P: "211 \r\n" P2C: "211 \r\n" C2P: "HELP USER\r\n" P2S: "HELP USER\r\n" S2P: "502 \r\n" P2C: "502 \r\n" C2P: "HELP USER\r\n" P2S: "HELP USER\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . #Miscellaneous commands #SITE S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "SITE HELP\r\n" P2S: "SITE HELP\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "SITE LISTUSER\r\n" P2S: "SITE LISTUSER\r\n" S2P: "202 \r\n" P2C: "202 \r\n" C2P: "SITE akarmi\r\n" P2S: "SITE akarmi\r\n" S2P: "501 \r\n" P2C: "501 \r\n" C2P: "SITE 123\r\n" P2S: "SITE 123\r\n" S2P: "530 \r\n" P2C: "530 \r\n" #C2P: "SITE\r\n" #P2C: "500 " $NIHIL "\r\n" . #NOOP S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "500 \r\n" P2C: "500 \r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "504 \r\n" P2C: "500 " $NIHIL "\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "421 \r\n" P2C: "421 Logoff\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.data_port_min = 40500 self.data_port_max = 40500 self.request["EPSV"] = (FTP_REQ_ACCEPT) self.request["EPRT"] = (FTP_REQ_ACCEPT) AbstractFtpProxy.loadAnswers(self) self.strict_port_checking = FALSE EndPolicy #EPSV S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "EPSV 1\r\n" P2S: "EPSV\r\n" S2P: "229 Entering Extended Passive Mode (|||40500|)\r\n" P2C: "229 Entering Extended Passive Mode (|||40500|)\r\n" C2P: "EPSV\r\n" P2S: "EPSV\r\n" S2P: "229 Entering Extended Passive Mode (|||40500|)\r\n" P2C: "229 Entering Extended Passive Mode (|||40500|)\r\n" C2P: "EPSV ALL\r\n" P2S: "EPSV\r\n" S2P: "229 \r\n" P2C: "229 \r\n" C2P: "EPSV 2\r\n" P2S: "EPSV\r\n" S2P: "501 \r\n" P2C: "501 \r\n" . #EPRT S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "EPRT |1|10.2.3.6|40500|\r\n" P2S: "EPRT |1|127.0.0.1|40500|\r\n" S2P: "200 Command OK_1\r\n" P2C: "200 Command OK_1\r\n" C2P: "EPRT |1|w3.org|40500|\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "EPRT |||40500|\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "EPRT |2|1080::8:800:200C:417A|40500|\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "EPRT |1080::8:800:200C:417A|40500|\r\n" P2C: "500 " $NIHIL "\r\n" C2P: "EPRT\r\n" P2C: "500 " $NIHIL "\r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/inband_auth.tests000066400000000000000000000050231172670260400242150ustar00rootroot00000000000000StartGlobalInfo Tags B-02303 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.transparent_mode = FALSE self.strict_port_checking = FALSE EndPolicy P2C: "220-Welcome in Zorp FTP proxy authentication module!\r\n\ Please authenticate yourself!\r\n\ Authentication form:\r\n\ USER @\r\n\ PASS \r\n\ 220 \r\n" C2P: "USER user@remotesite\r\n" P2C: "331 Username and host okay, send your password.\r\n" C2P: "PASS password@\r\n" S2P: "220 Udv\r\n" P2S: "USER user\r\n" S2P: "331 Send your password\r\n" P2S: "PASS password@\r\n" S2P: "530 Login incorrect\r\n" P2C: "530 Login incorrect\r\n" C2P: "USER user@remotesite\r\n" P2S: "USER user\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS password@aaa\r\n" P2S: "PASS password@aaa\r\n" S2P: "530 Login incorrect\r\n" P2C: "530 Login incorrect\r\n" C2P: "USER user@proxyuser@remotesite\r\n" P2S: "USER user\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS password@aaa\r\n" P2S: "PASS password\r\n" S2P: "530 Login incorrect\r\n" P2C: "530 Login incorrect\r\n" C2P: "USER user@proxyuser@othersite\r\n" P2C: "501 Inband routing information invalid\r\n" C2P: "USER user@othersite\r\n" P2C: "501 Inband routing information invalid\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 Goodbye.\r\n" P2C: "221 Goodbye.\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.transparent_mode = TRUE self.strict_port_checking = FALSE EndPolicy S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "USER username@remotesite\r\n" P2S: "USER username@remotesite\r\n" S2P: "331 User name okay, need password\r\n" P2C: "331 User name okay, need password\r\n" C2P: "PASS password@aaa\r\n" P2S: "PASS password@aaa\r\n" S2P: "530 Login incorrect\r\n" P2C: "530 Login incorrect\r\n" C2P: "USER username@othersite\r\n" P2S: "USER username@othersite\r\n" S2P: "331 User name okay, need password\r\n" P2C: "331 User name okay, need password\r\n" C2P: "PASS password@aaa\r\n" P2S: "PASS password@aaa\r\n" S2P: "530 Login incorrect\r\n" P2C: "530 Login incorrect\r\n" C2P: "USER username@proxyuser@remotesite\r\n" P2S: "USER username@proxyuser@remotesite\r\n" S2P: "331 User name okay, need password\r\n" P2C: "331 User name okay, need password\r\n" C2P: "PASS password@aaa\r\n" P2S: "PASS password@aaa\r\n" S2P: "530 Login incorrect\r\n" P2C: "530 Login incorrect\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 Goodbye.\r\n" P2C: "221 Goodbye.\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/ftp/func/cases/other.tests000066400000000000000000000110331172670260400230600ustar00rootroot00000000000000StartPolicy def config(self): FtpProxyRW.config(self) self.transparent_mode = FALSE self.strict_port_checking = FALSE EndPolicy #Bug 2986 #Accept all (unknown) command in preconnect state P2C: "220-Welcome in Zorp FTP proxy authentication module!\r\n\ Please authenticate yourself!\r\n\ Authentication form:\r\n\ USER @\r\n\ PASS \r\n\ 220 \r\n" C2P: "AUTH SASL\r\n" P2C: "502 " $NIHIL "\r\n" C2P: "USER user@remotesite\r\n" P2C: "331 Username and host okay, send your password.\r\n" . #testing FTP_DATA_PASSIVE and FTP_DATA_ACTIVE StartPolicy def config(self): FtpProxyRW.config(self) self.data_mode = FTP_DATA_PASSIVE self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1300 C2P: "PORT " %CLIENT_COMMAIP ",5,20\r\n" P2S: "PASV\r\n" PORT = 1289 SERVER_COMMAIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_COMMAIP ",5,9)\r\n" P2C: "200 " $NIHIL "\r\n" C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "150 Data Follow\r\n" S2P: Accept C2P: Accept dS2P: "barmi\r\n" P2C: "150 Data Follow\r\n" dP2C: "barmi\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.data_mode = FTP_DATA_ACTIVE self.strict_port_checking = FALSE EndPolicy StartInfo Tags qwer EndInfo S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "PASV\r\n" P2S: "PORT " $CIP1 "," $CIP2 "," $CIP3 "," $CIP4 "," $CPORT1 "," $CPORT2 "\r\n" S2P: "200 Ok\r\n" P2C: "227 Entering Passive mode (" $SIP1 "," $SIP2 "," $SIP3 "," $SIP4 "," $SPORT1 "," $SPORT2 ").\r\n" C2P: "RETR barmi\r\n" P2S: "RETR barmi\r\n" S2P: "150 Data follow\r\n" TMP_CLIENT_IP = str(CIP1)+'.'+str(CIP2)+'.'+str(CIP3)+'.'+str(CIP4) CLIENT_PORT = CPORT1*256+CPORT2 S2P: Connect TMP_CLIENT_IP CLIENT_PORT TMP_SERVER_IP = str(SIP1)+'.'+str(SIP2)+'.'+str(SIP3)+'.'+str(SIP4) SERVER_PORT = SPORT1*256+SPORT2 C2P: Connect TMP_SERVER_IP SERVER_PORT dS2P: "barmi\r\n" P2C: "150 Data follow\r\n" dP2C: "barmi\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy # testing aborted and reinitiated transfer S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,21\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1301 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150 Data Follow\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 Data Follow\r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226 Ok\r\n" P2C: "226 Ok\r\n" C2P: "PORT " %CLIENT_COMMAIP ",5,22\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1302 C2P: "RETR masik\r\n" P2S: "RETR masik\r\n" S2P: "150 Data Follow\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 Data Follow\r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,23\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1303 C2P: "RETR barmi\r\n" P2S: "RETR barmi\r\n" S2P: "150 Data Follow\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 Data Follow\r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "451 Error\r\n" P2C: "451 Error\r\n" C2P: "PORT " %CLIENT_COMMAIP ",5,24\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1304 C2P: "RETR barmi\r\n" P2S: "RETR barmi\r\n" S2P: "150 Data Follow\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150 Data Follow\r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect . zorp-3.9.5/tests/functional/ftp/func/cases/rfc3659.tests000066400000000000000000000064171172670260400230520ustar00rootroot00000000000000StartGlobalInfo Tags rfc3659 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE AbstractFtpProxy.loadAnswers(self) EndPolicy # MLST S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" C2P: "MLst src/install/windows.c\r\n" P2S: "MLST src/install/windows.c\r\n" S2P: "250-Listing src/install/windows.c\r\n\ type=file;perm=r;size=993; /misc/src/install/windows.c\r\n\ 250 End\r\n" P2C: "250-Listing src/install/windows.c\r\n\ type=file;perm=r;size=993; /misc/src/install/windows.c\r\n\ 250 End\r\n" C2P: "MLst SRC/INSTALL/WINDOWS.C\r\n" P2S: "MLST SRC/INSTALL/WINDOWS.C\r\n" S2P: "250-Listing SRC/INSTALL/WINDOWS.C\r\n\ type=file;perm=r;size=993; /misc/SRC/INSTALL/WINDOWS.C\r\n\ 250 End\r\n" P2C: "250-Listing SRC/INSTALL/WINDOWS.C\r\n\ type=file;perm=r;size=993; /misc/SRC/INSTALL/WINDOWS.C\r\n\ 250 End\r\n" C2P: "MLst NONEXISTENT.TXT\r\n" P2S: "MLST NONEXISTENT.TXT\r\n" S2P: "550 no such file or directory\r\n" P2C: "550 no such file or directory\r\n" C2P: "MLST\r\n" P2S: "MLST\r\n" S2P: "250-Begin\r\n\ type=dir;unique=AQkAAAAAAAABCAAA; /\r\n\ 250 End.\r\n" P2C: "250-Begin\r\n\ type=dir;unique=AQkAAAAAAAABCAAA; /\r\n\ 250 End.\r\n" . # MLSD S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1286 C2P: "PORT " %CLIENT_COMMAIP ",5,6\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "MLSD\r\n" P2S: "MLSD\r\n" S2P: "450 \r\n" P2C: "450 \r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: Listen 1286 C2P: "PORT " %CLIENT_COMMAIP ",5,6\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" S2P: "200 \r\n" P2C: "200 \r\n" C2P: "MLSD\r\n" P2S: "MLSD\r\n" S2P: "150 Opening ASCII mode data connection for MLS.\r\n" FW_IP = str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) FW_PORT = PORT1*256 + PORT2 S2P: Connect FW_IP FW_PORT C2P: Accept dS2P: "type=cdir;unique=AQkAAAAAAAABCAAA; /\r\n\ type=dir;unique=AQkAAAAAAAABEAAA; bin\r\n\ type=dir;unique=AQkAAAAAAAABGAAA; etc\r\n\ type=dir;unique=AQkAAAAAAAAB8AwA; halflife\r\n\ type=dir;unique=AQkAAAAAAAABoAAA; incoming\r\n\ type=dir;unique=AQkAAAAAAAABIAAA; lib\r\n\ type=dir;unique=AQkAAAAAAAABWAEA; linux\r\n\ type=dir;unique=AQkAAAAAAAABKAEA; ncftpd\r\n\ type=dir;unique=AQkAAAAAAAABGAEA; outbox\r\n\ type=dir;unique=AQkAAAAAAAABuAAA; quake2\r\n\ type=dir;unique=AQkAAAAAAAABQAEA; winstuff\r\n" P2C: "150 Opening ASCII mode data connection for MLS.\r\n" dP2C: "type=cdir;unique=AQkAAAAAAAABCAAA; /\r\n\ type=dir;unique=AQkAAAAAAAABEAAA; bin\r\n\ type=dir;unique=AQkAAAAAAAABGAAA; etc\r\n\ type=dir;unique=AQkAAAAAAAAB8AwA; halflife\r\n\ type=dir;unique=AQkAAAAAAAABoAAA; incoming\r\n\ type=dir;unique=AQkAAAAAAAABIAAA; lib\r\n\ type=dir;unique=AQkAAAAAAAABWAEA; linux\r\n\ type=dir;unique=AQkAAAAAAAABKAEA; ncftpd\r\n\ type=dir;unique=AQkAAAAAAAABGAEA; outbox\r\n\ type=dir;unique=AQkAAAAAAAABuAAA; quake2\r\n\ type=dir;unique=AQkAAAAAAAABQAEA; winstuff\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226 \r\n" P2C: "226 \r\n" . zorp-3.9.5/tests/functional/ftp/func/cases/transfer.tests000066400000000000000000000051771172670260400235770ustar00rootroot00000000000000StartGlobalInfo Tags bug10795 bug10920 bug10968 EndGlobalInfo StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE self.request_stack["*"] = (FTP_STK_DATA, "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_REJECT\\\\nn[]Description\\\\nnNem nyert\\\\nn\\\\nn'>&3; read alma <&3") EndPolicy # Some valid scenarios # testing aborted and reinitiated transfer S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,25\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1305 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150 Data Follow\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226-Ok\r\n226-Meg mindig Ok\r\n226 Itt a vege" P2C: "550 Data transfer failed (Nem nyert)\r\n" . StartPolicy def config(self): FtpProxyRW.config(self) self.strict_port_checking = FALSE EndPolicy S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,21\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1301 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150-Data Follow\r\n150-Kakukk\r\n150 Vege\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150-Data Follow\r\n Kakukk\r\n150 Vege\r\n" dP2C: "barmi\r\n" dS2P: Disconnect dP2C: Disconnect S2P: "226-Ok\r\n226 Vege\r\n" P2C: "226-Ok\r\n226 Vege\r\n" . S2P: "220 kesz\r\n" P2C: "220 kesz\r\n" C2P: "USER user\r\n" P2S: "USER user\r\n" S2P: "230 logged in\r\n" P2C: "230 logged in\r\n" CLIENT_COMMAIP = string.replace(ZTS_CLIENT_IP,".",",") C2P: "PORT " %CLIENT_COMMAIP ",5,22\r\n" P2S: "PORT " $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 "\r\n" PORT = PORT1*256 + PORT2 IP = str(IP1)+'.'+str(IP2)+'.'+str(IP3)+'.'+str(IP4) S2P: "200 Ok\r\n" P2C: "200 Ok\r\n" C2P: Listen 1302 C2P: "RETR egyik\r\n" P2S: "RETR egyik\r\n" S2P: "150-Data Follow\r\n150-Kakukk\r\n150 Vege\r\n" S2P: Connect IP PORT C2P: Accept dS2P: "barmi\r\n" P2C: "150-Data Follow\r\n Kakukk\r\n150 Vege\r\n" dP2C: "barmi\r\n" C2P: Disconnect P2S: Disconnect dP2S: Disconnect dP2C: Disconnect . zorp-3.9.5/tests/functional/ftp/func/cases/valid-scenarios.tests000066400000000000000000000064611172670260400250330ustar00rootroot00000000000000StartPolicy def config(self): FtpProxyRW.config(self) self.request["SMNT"] = (FTP_REQ_ACCEPT) self.request["REIN"] = (FTP_REQ_ACCEPT) self.strict_port_checking = FALSE AbstractFtpProxy.loadAnswers(self) EndPolicy # Some valid scenarios S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "USER username\r\n" P2S: "USER username\r\n" S2P: "331 User name okay, need password\r\n" P2C: "331 User name okay, need password\r\n" C2P: "PASS password\r\n" P2S: "PASS password\r\n" S2P: "332 Need account for login\r\n" P2C: "332 Need account for login\r\n" C2P: "ACCT account\r\n" P2S: "ACCT account\r\n" S2P: "230 User logged in, proceed\r\n" P2C: "230 User logged in, proceed\r\n" C2P: "CWD /home/test/\r\n" P2S: "CWD /home/test/\r\n" S2P: "250 Requested file action okay, completed\r\n" P2C: "250 Requested file action okay, completed\r\n" C2P: "CDUP\r\n" P2S: "CDUP\r\n" S2P: "200 Command okay\r\n" P2C: "200 Command okay\r\n" C2P: "SMNT server\r\n" P2S: "SMNT server\r\n" S2P: "250 Requested file action okay, completed\r\n" P2C: "250 Requested file action okay, completed\r\n" C2P: "REIN\r\n" P2S: "REIN\r\n" S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 \r\n" P2C: "221 \r\n" P2C: Disconnect . # PASS when no password is required # Bazsi 2004-01-23 # bug: 1642 S2P: "220 Service ready for new user\r\n" P2C: "220 Service ready for new user\r\n" C2P: "USER username\r\n" P2S: "USER username\r\n" S2P: "230 Authenticated, no password needed\r\n" P2C: "230 Authenticated, no password needed\r\n" C2P: "PASS password\r\n" P2S: "PASS password\r\n" S2P: "230 I said no password was needed\r\n" P2C: "230 I said no password was needed\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 \r\n" P2C: "221 \r\n" P2C: Disconnect . S2P: "220 Szia\r\n" P2C: "220 Szia\r\n" C2P: "USER ftp\r\n" P2S: "USER ftp\r\n" S2P: "331 Send your password\r\n" P2C: "331 Send your password\r\n" C2P: "PASS myself@\r\n" P2S: "PASS myself@\r\n" S2P: "230 Anonymous access granted\r\n" P2C: "230 Anonymous access granted\r\n" C2P: "PASV\r\n" P2S: "PASV\r\n" PORT=1290 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2C: "227 Entering Passive Mode (" $IP1 "," $IP2 "," $IP3 "," $IP4 "," $PORT1 "," $PORT2 ")\r\n" C_PORT=PORT1*256+PORT2 C_IP=str(IP1)+"."+str(IP2)+"."+str(IP3)+"."+str(IP4) C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "150 Data Follow\r\n" S2P: Accept C2P: Connect C_IP C_PORT dS2P: "barmi\r\n" P2C: "150 Data Follow\r\n" dP2C: "barmi\r\n" dC2P: "masikbarmi\r\n" dP2S: Nothing . StartPolicy def config(self): FtpProxyRW.config(self) self.transparent_mode = FALSE self.strict_port_checking = FALSE EndPolicy P2C: "220-Welcome in Zorp FTP proxy authentication module!\r\n\ Please authenticate yourself!\r\n\ Authentication form:\r\n\ USER @\r\n\ PASS \r\n\ 220 \r\n" C2P: "USER user@remotesite\r\n" P2C: "331 Username and host okay, send your password.\r\n" C2P: "PASS password@\r\n" S2P: "220 Udv\r\n" P2S: "USER user\r\n" S2P: "331 Send your password\r\n" P2S: "PASS password@\r\n" S2P: "230 Anonymous access granted\r\n" P2C: "230 Anonymous access granted\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 Goodbye.\r\n" P2C: "221 Goodbye.\r\n" . zorp-3.9.5/tests/functional/ftp/func/config000066400000000000000000000000001172670260400207350ustar00rootroot00000000000000zorp-3.9.5/tests/functional/ftp/info000066400000000000000000000002341172670260400175010ustar00rootroot00000000000000Base-Class: FtpProxyRW zorp-3.9.5/tests/functional/http/000077500000000000000000000000001172670260400170125ustar00rootroot00000000000000zorp-3.9.5/tests/functional/http/info000066400000000000000000000001041172670260400176630ustar00rootroot00000000000000Description: HTTP test suite Min-Version: 0.6 Base-Class: HttpProxy zorp-3.9.5/tests/functional/http/policy/000077500000000000000000000000001172670260400203115ustar00rootroot00000000000000zorp-3.9.5/tests/functional/http/policy/accepted-methods.tests000066400000000000000000000110231172670260400246030ustar00rootroot00000000000000 StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request["*"] = (HTTP_REQ_ACCEPT,) EndPolicy C2P: "OPTIONS /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "OPTIONS /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "GET /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "GET /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "HEAD /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "HEAD /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . # The response to a HEAD command *NEVER* contains an entity # Bug #4060 C2P: "HEAD /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "HEAD /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 1000\r\n\ \r\n" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 1000\r\n\ \r\n" . C2P: "POST /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "POST /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "PUT /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "PUT /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "DELETE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "DELETE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . #C2P: "TRACE /index.html HTTP/1.1\r\n\ #Host: www.foo.bar\r\n\ #\r\n" #P2S: "TRACE /index.html HTTP/1.1\r\n\ #Host: www.foo.bar\r\n\ #\r\n" #. # TRACE request / entity doesn't present in request #C2P: "TRACE /index.html HTTP/1.1\r\n\ #Host: www.foo.bar\r\n\ #Content-length: 13\r\n\ #\r\n\ #entitas teste" #P2C: "TRACE /index.html HTTP/1.1\r\n\ #Host: www.foo.bar\r\n\ #Content-length: 13\r\n\ #\r\n\" #. C2P: "PATCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "PATCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "LINK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "LINK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "UNLINK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "UNLINK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "PROPFIND /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "PROPFIND /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "PROPPATCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "PROPPATCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "MKCOL /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "MKCOL /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "COPY /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "COPY /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "MOVE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "MOVE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "NOTIFY /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "NOTIFY /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "POLL /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "POLL /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "SEARCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "SEARCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "SUBSCRIBE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "SUBSCRIBE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "UNSUBSCRIBE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "UNSUBSCRIBE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "LOCK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "LOCK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "UNLOCK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "UNLOCK /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "BCOPY /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "BCOPY /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "BDELETE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "BDELETE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "BMOVE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "BMOVE /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "BPROPFIND /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "BPROPFIND /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "BPROPPATCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2S: "BPROPPATCH /index.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" . C2P: "CONNECT www.foo.bar:443 HTTP/1.1\r\n\ Host: www.foo.bar:443\r\n\ \r\n" P2C: "HTTP/1.0 200 Connection established\r\n\r\n" . zorp-3.9.5/tests/functional/http/policy/dynamic/000077500000000000000000000000001172670260400217355ustar00rootroot00000000000000zorp-3.9.5/tests/functional/http/policy/dynamic/header-manip.tests000066400000000000000000000133601172670260400253560ustar00rootroot00000000000000StartGlobalInfo Tags bug5559 Min-Version 3.0 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request["POST"] = (HTTP_REQ_POLICY, self.checkRequestHeaderManip) self.response["POST", '404'] = (HTTP_REQ_POLICY, self.checkResponseHeaderManip) self.strict_header_checking = FALSE def checkRequestHeaderManip(self, method, url, version): if self.getRequestHeader('Content-Type') == 'ezegymimetipus01': self.setRequestHeader('Content-Type', 'ezegymimetipus02') return HTTP_REQ_ACCEPT elif self.getRequestHeader('Content-Type') == 'ezegymimetipus03': self.setRequestHeader('Kutykurutty', 'ezegymimetipus04') return HTTP_REQ_ACCEPT elif self.getRequestHeader('Content-Type') == 'ezegymimetipus05': self.setResponseHeader('Content-Type', 'ezegymimetipus06') return HTTP_REQ_ACCEPT elif self.getResponseHeader('Content-Type') == 'ezegymimetipus07': self.setRequestHeader('Content-Type', 'ezegymimetipus08') return HTTP_REQ_ACCEPT elif self.getRequestHeader('Content-Type') == 'ezegymimetipus13': return HTTP_REQ_ACCEPT elif self.getRequestHeader('Content-Type') == 'ezegymimetipus15': return HTTP_REQ_ACCEPT elif self.getRequestHeader('Content-Type') == 'ezegymimetipus07': return HTTP_REQ_ACCEPT return HTTP_REQ_REJECT def checkResponseHeaderManip(self, method, url, version, response): if self.getResponseHeader('Content-Type') == 'ezegymimetipus09': self.setResponseHeader('Content-Type', 'ezegymimetipus10') return HTTP_RSP_ACCEPT elif self.getResponseHeader('Content-Type') == 'ezegymimetipus11': self.setResponseHeader('Kutyimutyi', 'ezegymimetipus12') return HTTP_RSP_ACCEPT elif self.getResponseHeader('Content-Type') == 'ezegymimetipus13': self.setRequestHeader('Content-Type', 'ezegymimetipus14') return HTTP_RSP_ACCEPT elif self.getRequestHeader('Content-Type') == 'ezegymimetipus15': self.setResponseHeader('Content-Type', 'ezegymimetipus16') return HTTP_RSP_ACCEPT elif self.getResponseHeader('Content-Type') == 'ezegymimetipus05': return HTTP_RSP_ACCEPT elif self.getResponseHeader('Content-Type') == 'ezegymimetipus07': return HTTP_RSP_ACCEPT elif self.getResponseHeader('Content-Type') == 'ezegymimetipus17': return HTTP_RSP_ACCEPT return HTTP_RSP_REJECT EndPolicy QUOTE=chr(34) C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus01\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus02\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus09\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus10\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus03\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus03\r\n\ Kutykurutty: ezegymimetipus04\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus11\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus11\r\n\ Content-Length: 5\r\n\ Kutyimutyi: ezegymimetipus12\r\n\ \r\n\ 12345" . C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus05\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus05\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus05\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus05\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus07\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus07\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus07\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus07\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus13\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus13\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus13\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus13\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus15\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus15\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus17\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus16\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . zorp-3.9.5/tests/functional/http/policy/dynamic/mime-type.tests000066400000000000000000000035731172670260400247370ustar00rootroot00000000000000 StartGlobalInfo Tags bug4602 Min-Version 3.0 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request["POST"] = (HTTP_REQ_POLICY, self.checkMimeType) self.response["POST", "404"] = (HTTP_RSP_POLICY, self.checkMimeTypeResp) def checkMimeType(self, method, url, version): if self.request_mime_type != "ezegymimetipus": return HTTP_REQ_REJECT return HTTP_REQ_ACCEPT def checkMimeTypeResp(self, method, url, version, response): if self.response_mime_type != "ezegymimetipus2": return HTTP_RSP_REJECT return HTTP_RSP_ACCEPT EndPolicy QUOTE=chr(34) C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus; charset=" %QUOTE "utf8" %QUOTE "\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus; charset=" %QUOTE "utf8" %QUOTE "\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus2; charset=" %QUOTE "iso-8859-2" %QUOTE "\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 404 OK\r\n\ Content-Type: ezegymimetipus2; charset=" %QUOTE "iso-8859-2" %QUOTE "\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" C2P: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus; charset=" %QUOTE "utf8" %QUOTE "\r\n\ \r\n" P2S: "POST /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Type: ezegymimetipus; charset=" %QUOTE "utf8" %QUOTE "\r\n\ \r\n" S2P: "HTTP/1.1 404 OK\r\n\ Content-Type: ezmasmimetipus; charset=" %QUOTE "iso-8859-2" %QUOTE "\r\n\ Content-Length: 6\r\n\ \r\n\ qwerty" P2C: "HTTP/1.0 500 Error encountered\r\nConnection: close\r\n" $NIHIL "\n\n" . zorp-3.9.5/tests/functional/http/policy/dynamic/policy-calls.tests000066400000000000000000000032141172670260400254140ustar00rootroot00000000000000 StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request["GET"] = (HTTP_REQ_POLICY, self.filterURL) def filterURL(self, method, url, version): import re log("http.message", 7, "url:%s" % url,) if re.match(".*hubbabubba.*", url) != None: self.request_url = "http://www.index.hu/index.html" return HTTP_REQ_ACCEPT EndPolicy C2P: "GET /var/wwwhubbabubba/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /index.html HTTP/1.1\r\n\ Host: www.index.hu\r\n\ \r\n" . C2P: "GET /var/www/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index3.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" . # Keep-alive connection C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 35\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345elsofelesleg, sok-sok felesleg" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 35\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345elsofelesleg, sok-sok felesleg" C2P: "GET /var/www/index2.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index2.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 38\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345masodikfelesleg, sok-sok felesleg" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 38\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345masodikfelesleg, sok-sok felesleg" . zorp-3.9.5/tests/functional/http/policy/permit-both-conn-hdrs.tests000066400000000000000000000040301172670260400255150ustar00rootroot00000000000000 StartGlobalInfo Tags bug4072 bug4250 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.permit_both_connection_headers = TRUE self.permit_proxy_requests = TRUE EndPolicy C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" . C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ Proxy-Connection: close\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" . C2P: "GET http://www.net.org/var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: close\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ Proxy-Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" . C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ Proxy-Connection: close\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Proxy-Connection: close\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ Connection: keep-alive\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" . zorp-3.9.5/tests/functional/http/policy/permit-invalid-hex-escape.tests000066400000000000000000000010161172670260400263370ustar00rootroot00000000000000StartGlobalInfo Tags qqq EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.permit_invalid_hex_escape = TRUE EndPolicy C2P: "GET /web/v2/portal.nsf/kapcsolatok_hu?OpenView&ExpandView&Count=500%1%20x HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /web/v2/portal.nsf/kapcsolatok_hu?OpenView&ExpandView&Count=500%1%20x HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ \r\n\ 123456789012" . zorp-3.9.5/tests/functional/http/policy/permit-unicode-url.tests000066400000000000000000000007271172670260400251270ustar00rootroot00000000000000StartGlobalInfo Tags unicode EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.permit_unicode_url = TRUE EndPolicy C2P: "GET /var/www/inde%u0078.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ \r\n\ 123456789012" . zorp-3.9.5/tests/functional/http/policy/rejected-methods.tests000066400000000000000000000006631172670260400246300ustar00rootroot00000000000000StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request["PUT"] = (HTTP_REQ_REJECT,) self.request["GET"] = (HTTP_REQ_ABORT,) EndPolicy C2P: "PUT /foo.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2C: "HTTP/1." $VER " 500" $NIHIL "\n" . C2P: "GET /foo.html HTTP/1.1\r\n\ Host: www.foo.bar\r\n\ \r\n" P2C: "HTTP/1." $VER " 500" $NIHIL "\n" P2C: Disconnect . zorp-3.9.5/tests/functional/http/policy/rewrite_url.tests000066400000000000000000000015141172670260400237410ustar00rootroot00000000000000StartGlobalInfo Tags halaszg EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.max_url_length = 200 self.parent_proxy = "172.16.80.1" self.parent_proxy_port = 8080 self.permit_proxy_requests = TRUE self.rewrite_host_header = TRUE self.request["GET"] = (HTTP_REQ_POLICY, self.filterURL) self.transparent_mode = FALSE def filterURL(self, method, url, version): self.request_url = 'http://owa.domain.int/exchange/' return HTTP_REQ_ACCEPT EndPolicy C2P: "GET http://www.kakukk.bu/ HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" P2S: "GET http://owa.domain.int/exchange/ HTTP/1.0\r\n\ Host: owa.domain.int\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/policy/timeout.tests000066400000000000000000000011311172670260400230570ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout = 3000 EndPolicy C2P: "GET /foo.html HTTP/1.1\r\n" A=time.sleep(6) C2P: "Host: www\r\n\ \r\n" P2C: "HTTP/1." $VER " 500" $NIHIL "\n" P2C: Disconnect . C2P: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 7\r\n\ \r\n" A=time.sleep(6) S2P: "aaaaa\r\n" P2C: "HTTP/1." $VER " 500" $NIHIL "\n" P2C: Disconnect . zorp-3.9.5/tests/functional/http/policy/use_canonical.tests000066400000000000000000000033621172670260400242040ustar00rootroot00000000000000StartGlobalInfo Tags bug7318 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.use_canonicalized_urls = TRUE self.transparent_mode = FALSE self.parent_proxy = '1' self.request["POST"] = (HTTP_REQ_POLICY, self.filterURL) def filterURL(self, method, url, version): self.request_url = 'http://owa.domain.int/exch%61nge/' return HTTP_REQ_ACCEPT EndPolicy C2P: "GET http://www.kakukk.bu/valami%2ecgi HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" P2S: "GET http://www.kakukk.bu/valami.cgi HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" . C2P: "POST http://www.kakukk.bu/valami%2ecgi HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" P2S: "POST http://owa.domain.int/exchange/ HTTP/1.0\r\n\ Host: owa.domain.int\r\n\ \r\n" . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.use_canonicalized_urls = FALSE self.transparent_mode = FALSE self.parent_proxy = '1' self.request["POST"] = (HTTP_REQ_POLICY, self.filterURL) def filterURL(self, method, url, version): self.request_url = 'http://owa.domain.int/exch%61nge/' return HTTP_REQ_ACCEPT EndPolicy C2P: "GET http://www.kakukk.bu/valami%2ecgi HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" P2S: "GET http://www.kakukk.bu/valami%2ecgi HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" . C2P: "POST http://www.kakukk.bu/valami%2ecgi HTTP/1.0\r\n\ Host: www.kakukk.bu\r\n\ \r\n" P2S: "POST http://owa.domain.int/exch%61nge/ HTTP/1.0\r\n\ Host: owa.domain.int\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/policy/use_default_port.tests000066400000000000000000000050141172670260400247410ustar00rootroot00000000000000StartGlobalInfo Tags bug6315 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = TRUE self.target_port_range = '108' def setServerAddress(self, host, port): if host == 'www.kakukk.bu' and port == 80: return HttpProxy.setServerAddress(self, host, port) proxyLog(self, CORE_DEBUG, 0, "incorrect server address; host='%s', port='%d'", (host, port)) return None EndPolicy C2P: "GET / HTTP/1.0\r\n\ Host: www.kakukk.bu:8080\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www.kakukk.bu:8080\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = TRUE self.use_default_port_in_transparent_mode = FALSE self.target_port_range = '8080' def setServerAddress(self, host, port): if host == 'www.kakukk.bu' and port == 8080: return HttpProxy.setServerAddress(self, host, port) proxyLog(self, CORE_DEBUG, 0, "incorrect server address; host='%s', port='%d'", (host, port)) return None EndPolicy C2P: "GET / HTTP/1.0\r\n\ Host: www.kakukk.bu:8080\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www.kakukk.bu:8080\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" . C2P: "GET / HTTP/1.0\r\n\ Host: www.kakukk.bu:80\r\n\ \r\n" P2C: "HTTP/1.0 502 Error encountered\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ \r\n\ \n" $NIHIL "\n\n" . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = TRUE self.parent_proxy = '1' self.parent_proxy_port = 3128 self.permit_proxy_requests = TRUE def setServerAddress(self, host, port): if host == '1' and port == 3128: return HttpProxy.setServerAddress(self, host, port) proxyLog(self, CORE_DEBUG, 0, "incorrect server address; host='%s', port='%d'", (host, port)) return None EndPolicy C2P: "GET http://www.kakukk.bu:8080/ HTTP/1.0\r\n\ Host: www.kakukk.bu:8080\r\n\ \r\n" P2S: "GET http://www.kakukk.bu:8080/ HTTP/1.0\r\n\ Host: www.kakukk.bu:8080\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" . zorp-3.9.5/tests/functional/http/protocol/000077500000000000000000000000001172670260400206535ustar00rootroot00000000000000zorp-3.9.5/tests/functional/http/protocol/basic.tests000066400000000000000000000176301172670260400230270ustar00rootroot00000000000000 StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy QUOTE = chr(34) # Valid scenario with authenticate C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ Allow: GET, HEAD\r\n\ Accept: text/plain; text/html\r\n\ Content-Encoding: x-gzip\r\n\ Content-Length: 14\r\n\ Content-Range: bytes 21010-47021/47022\r\n\ Content-Type: text/html\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ From: webmaster@w3.org\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\ Pragma: no-cache\r\n\ Referer: http://www.w3.org/hypertext/DataSources/Overview.html\r\n\ User-Agent: CERN-LineMode/2.15 libwww/2.17b3\r\n\ \r\n\ request entity" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Connection: keep-alive\r\n\ Allow: GET, HEAD\r\n\ Accept: text/plain; text/html\r\n\ Content-Encoding: x-gzip\r\n\ Content-Length: 14\r\n\ Content-Range: bytes 21010-47021/47022\r\n\ Content-Type: text/html\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ From: webmaster@w3.org\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\ Pragma: no-cache\r\n\ Referer: http://www.w3.org/hypertext/DataSources/Overview.html\r\n\ User-Agent: CERN-LineMode/2.15 libwww/2.17b3\r\n\ \r\n\ request entity" S2P: "HTTP/1.1 401 Authorization Required\r\n\ WWW-Authenticate: Basic realm=" %QUOTE "egy realm" %QUOTE "\r\n\ Content-Length: 20\r\n\ Connection: keep-alive\r\n\ \r\n\ Please authenticate!" P2C: "HTTP/1.1 401 Authorization Required\r\n\ WWW-Authenticate: Basic realm=" %QUOTE "egy realm" %QUOTE "\r\n\ Content-Length: 20\r\n\ Connection: keep-alive\r\n\ \r\n\ Please authenticate!" C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Allow: GET, HEAD\r\n\ Authorization: Basic 889t98hgetuogihtg948hg49hpg\r\n\ Content-Encoding: x-gzip\r\n\ Content-Length: 14\r\n\ Content-Type: text/html\r\n\ Date: Tue, 15 Nov 1994 08:22:31 GMT\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ From: webmaster@w3.org\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\ Pragma: no-cache\r\n\ Referer: http://www.w3.org/hypertext/DataSources/Overview.html\r\n\ User-Agent: CERN-LineMode/2.15 libwww/2.17b3\r\n\ \r\n\ request entity" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Allow: GET, HEAD\r\n\ Authorization: Basic 889t98hgetuogihtg948hg49hpg\r\n\ Content-Encoding: x-gzip\r\n\ Content-Length: 14\r\n\ Content-Type: text/html\r\n\ Date: Tue, 15 Nov 1994 08:22:31 GMT\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ From: webmaster@w3.org\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\ Pragma: no-cache\r\n\ Referer: http://www.w3.org/hypertext/DataSources/Overview.html\r\n\ User-Agent: CERN-LineMode/2.15 libwww/2.17b3\r\n\ \r\n\ request entity" S2P: "HTTP/1.1 200 OK\r\n\ Allow: GET, HEAD\r\n\ Connection: keep-alive\r\n\ Content-Encoding: x-gzip\r\n\ Content-Length: 15\r\n\ Content-Type: text/html\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\ Location: http://www.net.org/hypertext/WWW/NewLocation.html\r\n\ Pragma: no-cache\r\n\ Server: CERN/3.0 libwww/2.17\r\n\ WWW-Authenticate: Basic realm=" %QUOTE "egy realm" %QUOTE "\r\n\ \r\n\ response entity" P2C: "HTTP/1.1 200 OK\r\n\ Allow: GET, HEAD\r\n\ Connection: keep-alive\r\n\ Content-Encoding: x-gzip\r\n\ Content-Length: 15\r\n\ Content-Type: text/html\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\ Location: http://www.net.org/hypertext/WWW/NewLocation.html\r\n\ Pragma: no-cache\r\n\ Server: CERN/3.0 libwww/2.17\r\n\ WWW-Authenticate: Basic realm=" %QUOTE "egy realm" %QUOTE "\r\n\ \r\n\ response entity" . #Content codings C2P: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Content-Encoding: x-compress\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Content-Encoding: x-compress\r\n\ \r\n" . C2P: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Accept-Encoding: identity\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Accept-Encoding: identity\r\n\ \r\n" . C2P: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Content-Type: text\r\n\ Content-Encoding: X-gZip\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Content-Type: text\r\n\ Content-Encoding: X-gZip\r\n\ \r\n" . # Content charset, language and type C2P: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Accept-Charset: iso-8859-5, unicode-1-1\r\n\ Accept-Language: da, en\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ Accept-Charset: iso-8859-5, unicode-1-1\r\n\ Accept-Language: da, en\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ Content-Length: 4\r\n\ Content-Language: en\r\n\ \r\n\ abcd" P2C: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ Content-Length: 4\r\n\ Content-Language: en\r\n\ \r\n\ abcd" . # Invalid version C2P: "GET /var/www/index.html HTTP/1.qwer\r\n\ Host: www.net.org\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ " $NIHIL "\n" . # HTTP/0.9 / Simple request C2P: "GET /pub/var/index.html\r\n" P2C: "\n\ " $NIHILA "\ 'Host:' header is required, and HTTP/0.9 can't transfer headers.\ " $NIHILB "\n" . # Invalid HTTP messages / missing null line # #C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #Contetnt-Length: 12\r\n\ #Location: http://www.w3.org/hypertext/WWW/NewLocation.html\r\n" #P2C: "HTTP/1.0 500 Error encountered" $NIHIL "\n" #. # Request header field in a response #C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #Content-Length: 12\r\n\ #\r\n\ #entitas test" # #P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #Content-Length: 12\r\n\ #\r\n\ #entitas test" # #S2P: "HTTP/1.0 200 OK\r\n\ #Allow: GET, HEAD\r\n\ #Content-Length: 19\r\n\ #Location: http://www.w3.org/hypertext/WWW/NewLocation.html\r\n\ #From: webmaster@w3.org\r\n\ #Connection: close\r\n\ #\r\n\ #masik entitas teste" # #P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "" #. # # Response header field in a request #C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #Location: http://www.w3.org/hypertext/WWW/NewLocation.html\r\n\ #Content-Length: 12\r\n\ #\r\n\ #entitas test" #P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" #. # URI variation C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" . C2P: "GET http://www.balabit.hu/ HTTP/1.1\r\n\ Host: www.balabit.hu\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered" $NIHIL "\n" . C2P: "GET http://www.balabit.hu/ HTTP/1.0\r\n\ Host: www.balabit.hu\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered" $NIHIL "\n" . #C2P: "GET http://www.balabit.hu\r\n" #P2C: "\n\ #\n\ #Invalid request\n\ #\n\ #\n\ #

Your browser sent a request this gateway didn't understand.

\n\ #" $NIHIL "\n" #. # Text search C2P: "GET /var/www/index.html?abc HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html?abc HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 3\r\n\ Connection: close\r\n\ \r\n\ abc" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 3\r\n\ Connection: close\r\n\ \r\n\ abc" . #C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #\r\n\ #entitas test\r\n" # #P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #\r\n\ #entitas test" #. # POST request / Content-length is mandatory #C2P: "POST /pub/var/index.html HTTP/1.0\r\n\ #Host: www.w3.org\r\n\ #\r\n\ #abc" #P2C: "HTTP/1.0 400 Error encountered\r\n" $NIHIL "\n" #. zorp-3.9.5/tests/functional/http/protocol/bug13705.tests000066400000000000000000000026531172670260400231220ustar00rootroot00000000000000StartGlobalInfo Tags bug13705 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy # In case of version 1.0 Zorp should translate # unknown Connection header to Connection: close C2P: "GET /1.0.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: almafa\r\n\ \r\n" P2S: "GET /1.0.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: close\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . # In case of version 1.1 Zorp should (in this version) # change unknown Connection header to Connection: keep-alive # Client side C2P: "GET /1.0.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: almafa\r\n\ \r\n" P2S: "GET /1.0.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . # Server side. C2P: "GET /1.0.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET /1.0.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: almafa\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" . zorp-3.9.5/tests/functional/http/protocol/changing-host-headers.tests000066400000000000000000000035771172670260400261150ustar00rootroot00000000000000 StartGlobalInfo Tags bug6177 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.connected = 0 def connectServer(self): if not self.connected: self.connected = 1 return HttpProxy.connectServer(self) EndPolicy #simple HTTP/0.9 request and response C2P: "GET /foo.html HTTP/1.0\r\n\ Host: picurka.hu\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.0\r\n\ Host: picurka.hu\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 10\r\n\ Connection: keep-alive\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 10\r\n\ Connection: keep-alive\r\n\ \r\n\ 1234567890" C2P: "GET /foo.html HTTP/1.0\r\n\ Host: picurka2.hu\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.0\r\n\ Host: picurka2.hu\r\n\ Connection: keep-alive\r\n\ \r\n" . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = 0 self.connected = 0 def connectServer(self): if not self.connected: self.connected = 1 return HttpProxy.connectServer(self) EndPolicy #simple HTTP/0.9 request and response C2P: "GET http://picurka.hu/foo.html HTTP/1.0\r\n\ Host: picurka.hu\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.0\r\n\ Host: picurka.hu\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 10\r\n\ Connection: keep-alive\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 10\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n\ 1234567890" C2P: "GET http://picurka2.hu/foo.html HTTP/1.0\r\n\ Host: picurka2.hu\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" P2C: "HTTP/1.0 502 Error encountered\r\n\ Proxy-Connection: close\r\n\ Content-Type: text/html\r\n\ \r\n\ \n" $NIHIL "\n\n" . zorp-3.9.5/tests/functional/http/protocol/content-length.tests000066400000000000000000000037671172670260400247050ustar00rootroot00000000000000StartGlobalInfo Tags content-length EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy # Content length scenarios C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: 0\r\n\ \r\n\ entitas test\" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: 0\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 19\r\n\ Connection: close\r\n\ \r\n\ masik entitas teste" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 19\r\n\ Connection: close\r\n\ \r\n\ masik entitas teste" . C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: 12\r\n\ \r\n\ entitas test\r\n\ Expires: Thu, 01 Dec 1994 16:00:00 GMT\r\n\ From: webmaster@w3.org\r\n\ If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT\r\n\ Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT\r\n\" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: 12\r\n\ \r\n\ entitas test" . C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: 1024\r\n\ \r\n"\ "%s"x512 P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: 1024\r\n\ \r\n"\ "%s"x512 . C2P: "GET /var/www/index3.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index3.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345 a tobbi csak felesleg" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Content-Length: bubba\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . StartInfo Tags bug6533 EndInfo C2P: "GET /var/www/index3.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index3.html HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 302 OK\r\n\ Content-Length: 0\r\n\ \r\n" P2C: "HTTP/1.0 302 OK\r\n\ Content-Length: 0\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/protocol/headers.tests000066400000000000000000000021441172670260400233530ustar00rootroot00000000000000StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.strict_header_checking=TRUE EndPolicy # Header and value rules #nincs-space-a-kettospont-es-az-ertek-kozott C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via:ertek\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: ertek\r\n\ \r\n" . #space-van-a-nev-es-a-kettospont-kozott C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via :ertek\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: ertek\r\n\ \r\n" . C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ tspecial()%van: ertek\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . zorp-3.9.5/tests/functional/http/protocol/host-header.tests000066400000000000000000000013601172670260400241420ustar00rootroot00000000000000StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy # Host field scenarios C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.%.org\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: \r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Allow: GET, HEAD\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered" $NIHIL "\n" . zorp-3.9.5/tests/functional/http/protocol/http-0.9.tests000066400000000000000000000026341172670260400232270ustar00rootroot00000000000000## # Test HTTP/0.9 fallback when client requests HTTP/0.9 # Tests: bug 3320 # StartGlobalInfo Tags bug3320 http09 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.require_host_header = FALSE self.permit_http09_responses = TRUE EndPolicy #simple HTTP/0.9 request and response C2P: "GET /foo.html\r\n" P2S: "GET /foo.html\r\n" S2P: "entity body\r\n" P2C: "entity body\r\n" S2P: Disconnect P2C: Disconnect . #HTTP 1.0 request and HTTP 0.9 response C2P: "GET /foo.html HTTP/1.0\r\n\ Host: www\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.0\r\n\ Host: www\r\n\ \r\n" S2P: "This is a simple response without headers.\r\n" P2C: "This is a simple response without headers.\r\n" S2P: Disconnect P2C: Disconnect . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.require_host_header = FALSE self.permit_http09_responses = TRUE self.keep_persistent = TRUE EndPolicy StartInfo Tags bug9360 EndInfo C2P: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "This is a simple response without headers.\r\n" P2C: "This is a simple response without headers.\r\n" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/inband-auth.tests000066400000000000000000000031201172670260400241250ustar00rootroot00000000000000StartGlobalInfo Tags bug7646 EndGlobalInfo StartPolicy from Zorp.AuthDB import AbstractAuthenticationBackend class SimpleAuth(AbstractAuthenticationBackend): def getMethods(self, session_id, entity): from Zorp.AuthDB import Z_AUTH_METHODS return (Z_AUTH_METHODS, [('Method', 'PASSWD.internal:11:0:Password authentication')]) def setMethod(self, session_id, method): from Zorp.AuthDB import Z_AUTH_REQUEST return (Z_AUTH_REQUEST, []) def converse(self, session_id, credentials): from Zorp.AuthDB import Z_AUTH_ACCEPT return (Z_AUTH_ACCEPT, [('Group', 'aaa'), ('Group', 'bbb')]) auth_provider = SimpleAuth() def config(self): HttpProxy.config(self) self.timeout_request=30000 self.auth = self.auth_provider def userAuthenticated(self, entity, groups, auth_info): if 'aaa' not in groups or 'bbb' not in groups: raise AuthException, 'nincs meg az aaa/bbb csoport' EndPolicy QUOTE = chr(34) C2P: "GET / HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" P2C: "HTTP/1.0 401 Authentication is required.\r\n\ Connection: close\r\n\ WWW-Authenticate: Basic realm=" %QUOTE "Zorp HTTP auth" %QUOTE "\r\n\ Content-Type: text/html\r\n\ \r\n\ " $NIHIL "\n" . C2P: "GET / HTTP/1.0\r\n\ Host: www.net.org\r\n\ Authorization: Basic Y2ljYTptYWNza2E=\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ \r\n\ abcdef\r\n" S2P: Disconnect P2C: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ \r\n\ abcdef\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/keep-persistent.tests000066400000000000000000000033501172670260400250620ustar00rootroot00000000000000 StartGlobalInfo Tags keep-persistent EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.keep_persistent = TRUE EndPolicy # persistent request, non-persistent response, client side remains # persistent C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" S2P: Disconnect C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" . # persistent request, non-persistent response without content-length # proxy changes to chunked encoding C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Connection: keep-alive\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 5\r\n\ 12345\r\n" S2P: Disconnect P2C: "0\r\n\r\n" C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" . # persistent request, non-persistent response without content-length # proxy cannot change to chunked encoding as the client only supports # HTTP/1.0 C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ \r\n\ 12345" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/limit-checks.tests000066400000000000000000000023111172670260400243100ustar00rootroot00000000000000 # "Max length" scenarios StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.max_line_length = 40 self.max_hostname_length = 12 self.max_url_length = 20 self.max_header_lines = 3 EndPolicy C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.network.org\r\n\ \r\n" P2C: "HTTP" $NIHIL "\n" . C2P: "GET /index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 7\r\n\ Content-Language: barmi1barmi2barmi3barmi4\r\n\ \r\n\ content" P2C: "HTTP" $NIHIL "\n" . C2P: "GET /index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 7\r\n\ Content-MD5: Q2hlY2sgSW50Z==\r\n\ Content-Language: barmi1barmi2\r\n\ \r\n\ content" P2C: "HTTP" $NIHIL "\n" . C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OKezitttobbmintharminckarakterlesz\r\n\ Connection: close\r\n\ \r\n\ entitas nagyon" P2C: Disconnect . # max_url_length C2P: "GET /var/abcdef/ghijklmnop/qrstuvwxyz/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n\" P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/no-entity.tests000066400000000000000000000075651172670260400237020ustar00rootroot00000000000000StartGlobalInfo Tags suppress-data EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy # * anything that comes back with 204, 304 never has a data block even if it # has a Content-Length header # * anything that comes back with 205, does not have a data body unless # explicitly specified via a Content-Length or Content-Transfer-Encoding # headers C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 204 No Content\r\n\ Connection: close\r\n\ \r\n\ ez mar nem lehet itt!" P2C: "HTTP/1.1 204 No Content\r\n\ Connection: close\r\n\ \r\n" . # HEAD request / entity doesn't present in response C2P: "HEAD /pub/var/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "HEAD /pub/var/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ \r\n\ entitas teste" P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ \r\n" . # HEAD request / entity doesn't present in response even if it has a content-length header C2P: "HEAD /pub/var/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "HEAD /pub/var/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Content-Length: 10\r\n\ \r\n\ entitas teste" P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Content-Length: 10\r\n\ \r\n" . # HTTP response with 304 code may not present C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 304 Not Modified\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Etag: gonkyyy;1234\r\n\ Content-Location: index.html\r\n\ Vary: negotiate, accept, accept-language\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ Connection: close\r\n\ \r\n\ ez sem lesz itt!" P2C: "HTTP/1.1 304 Not Modified\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Etag: gonkyyy;1234\r\n\ Content-Location: index.html\r\n\ Vary: negotiate, accept, accept-language\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ Connection: close\r\n\ \r\n" . # HTTP response with 304 code may not present C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 304 Not Modified\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Etag: gonkyyy;1234\r\n\ Content-Location: index.html\r\n\ Vary: negotiate, accept, accept-language\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ Content-Length: 16\r\n\ Connection: close\r\n\ \r\n\ ez sem lesz itt!" P2C: "HTTP/1.1 304 Not Modified\r\n\ Date: Tue, 15 Nov 1994 08:12:31 GMT\r\n\ Etag: gonkyyy;1234\r\n\ Content-Location: index.html\r\n\ Vary: negotiate, accept, accept-language\r\n\ Expires: Thu, 01 Dec 2003 16:00:00 GMT\r\n\ Content-Length: 16\r\n\ Connection: close\r\n\ \r\n" . StartInfo Tags bug9500 EndInfo C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 205 Reset content\r\n\ Connection: close\r\n\ Content-Length: 77\r\n\ \r\n\ ez itt van annak ellenere, hogy defaultbol a 205-nel nem lehet itt adatresz!" P2C: "HTTP/1.1 205 Reset content\r\n\ Connection: close\r\n\ Content-Length: 77\r\n\ \r\n\ ez itt van annak ellenere, hogy defaultbol a 205-nel nem lehet itt adatresz!" . StartInfo Tags bug9500 EndInfo C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.w3.org\r\n\ \r\n" S2P: "HTTP/1.1 205 Reset content\r\n\ Connection: close\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 15\r\n\ ez mar nem lehet itt!\r\n\ 0\r\n\r\n" P2C: "HTTP/1.1 205 Reset content\r\n\ Connection: close\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 15\r\n\ ez mar nem lehet itt!\r\n\ 0\r\n\r\n" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/000077500000000000000000000000001172670260400240045ustar00rootroot00000000000000zorp-3.9.5/tests/functional/http/protocol/non-transparent/authenticated-connect.tests000066400000000000000000000014671172670260400313510ustar00rootroot00000000000000StartGlobalInfo Tags bug5356 Min-Version 3.0 EndGlobalInfo StartPolicy def config(self): from Zorp.AuthDB import AbstractAuthenticationBackend HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = FALSE self.request["CONNECT"] = (HTTP_REQ_ACCEPT,) self.target_port_range = "80-2000" # always fails self.auth = AbstractAuthenticationBackend() EndPolicy QUOTE = chr(34) C2P: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" P2C: "HTTP/1.0 407 Authentication is required.\r\n\ Proxy-Connection: close\r\n\ Proxy-Authenticate: Basic realm=" %QUOTE "Zorp HTTP auth" %QUOTE "\r\n\ Content-Type: text/html\r\n\ \r\n\ " $NIHIL "\n" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/authenticated-proxy-connect.tests000066400000000000000000000033221172670260400325200ustar00rootroot00000000000000StartGlobalInfo Tags bug5763 Min-Version 3.0 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.parent_proxy = '1' self.transparent_mode = FALSE self.request["CONNECT"] = (HTTP_REQ_ACCEPT,) self.target_port_range = "80-2000" EndPolicy C2P: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" P2S: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" S2P: "HTTP/1.1 200 Connection established\r\n\ \r\n" P2C: "HTTP/1.1 200 Connection established\r\n\ \r\n" C2P: "blablabla" S2P: "bliblibli" P2C: "bliblibli" P2S: "blablabla" . C2P: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" P2S: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" S2P: "HTTP/1.1 407 Authentication required\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 407 Authentication required\r\n\ Proxy-Connection: close\r\n\ \r\n\ 12345" . C2P: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ Proxy-Authenticate: Basic 889t98hgetuogihtg948hg49hpg\r\n\ \r\n" P2S: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ Proxy-Authenticate: Basic 889t98hgetuogihtg948hg49hpg\r\n\ \r\n" S2P: "HTTP/1.1 200 Connection established\r\n\ \r\n" P2C: "HTTP/1.1 200 Connection established\r\n\ \r\n" C2P: "blablabla" S2P: "bliblibli" P2C: "bliblibli" P2S: "blablabla" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/connection-rewrite.tests000066400000000000000000000017251172670260400307130ustar00rootroot00000000000000 StartGlobalInfo Tags bug4141 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = FALSE self.default_port = server_port self.target_port_range = "80-2000" EndPolicy # bugfix 4141 C2P: "GET http://" %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "/var/www/index.html HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n\ 12345" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/direct-connect.tests000066400000000000000000000010771172670260400277760ustar00rootroot00000000000000StartGlobalInfo Tags bug5763 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = FALSE self.request["CONNECT"] = (HTTP_REQ_ACCEPT,) self.target_port_range = "80-2000" EndPolicy C2P: "CONNECT " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT " HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" P2C: "HTTP/1.0 200 Connection established\r\n\ \r\n" C2P: "blablabla" S2P: "bliblibli" P2C: "bliblibli" P2S: "blablabla" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/ftp-over-http.tests000066400000000000000000000136321172670260400276140ustar00rootroot00000000000000StartGlobalInfo Tags ftpoverhttp bug5126 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = FALSE self.default_port = server_port self.permit_ftp_over_http = TRUE self.target_port_range = "21" EndPolicy # Simple directoty list. C2P: "GET ftp://ftp.szerver.hu/ HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /\r\n" S2P: "250 Requested file action okay, completed\r\n" P2S: "TYPE A\r\n" S2P: "200 Representation type is ASCII\r\n" P2S: "PASV\r\n" PORT=1290 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,10)\r\n" P2S: "LIST\r\n" S2P: "150 Data Follow\r\n" S2P: Accept dS2P: "-rw-r--r-- 1 root root 11 Jan 14 14:09 README\r\n\ -rw-r--r-- 1 root root 270 Mar 15 2001 makendx.pl\r\n\ drwxr-xr-x 11 root root 87132 Jul 3 2002 rfc\r\n\ drwxr-xr-x 2 sasa sasa 297 Jul 9 2004 sasa\r\n\ drwxr-xr-x 2 root root 82 Nov 4 2003 solaris\r\n" dS2P: Disconnect P2C: "HTTP/1.0 200 OK\r\n\ Content-Type: text/html\r\n\ Proxy-Connection: close\r\n\ \r\n" $NIHIL "" . # Simple file download C2P: "GET ftp://ftp.szerver.hu/something.txt HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /something.txt\r\n" S2P: "550 sdgfdgd: No such file or directory\r\n" #P2S: "CWD /\r\n" #S2P: "250 Requested file action okay, completed\r\n" P2S: "TYPE I\r\n" S2P: "200 Representation type is Binary\r\n" P2S: "PASV\r\n" PORT=1291 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,11)\r\n" P2S: "RETR something.txt\r\n" S2P: "150 Data Follow\r\n" S2P: Accept dS2P: "En vagyok a file." dS2P: Disconnect P2C: "HTTP/1.0 200 OK\r\n\ Content-Type: application/octet-stream\r\n\ Proxy-Connection: close\r\n\ \r\n\ En vagyok a file." . # Directory list for non existance directory C2P: "GET ftp://ftp.szerver.hu/nonexists/ HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /nonexists\r\n" #FIXMEE ??? S2P: "550 nonexists.txt: Not a directory\r\n" #S2P: "550 nonexists: No such file or directory\r\n" P2C: "HTTP/1.0 500 Error encountered\r\nProxy-Connection: close\r\nContent-Type: text/html\r\n\r\n" $NIHIL "\n" . # Download non-existing file. C2P: "GET ftp://ftp.szerver.hu/nonexists.txt HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /nonexists.txt\r\n" S2P: "550 nonexists.txt: Not a directory\r\n" P2S: "TYPE I\r\n" S2P: "200 Representation type is Binary\r\n" P2S: "PASV\r\n" PORT=1293 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,13)\r\n" P2S: "RETR nonexists.txt\r\n" S2P: "550 nonexists.txt: No such file or directory\r\n" P2C: "HTTP/1.0 500 Error encountered\r\nProxy-Connection: close\r\nContent-Type: text/html\r\n\r\n" $NIHIL "\n" . # Download file from sub directory. C2P: "GET ftp://ftp.szerver.hu/almafa/korte HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /almafa/korte\r\n" S2P: "550 /almafa/korte: Not a directory\r\n" P2S: "CWD /almafa\r\n" S2P: "250 Requested file action okay, completed\r\n" P2S: "TYPE I\r\n" S2P: "200 Representation type is Binary\r\n" P2S: "PASV\r\n" PORT=1294 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,14)\r\n" P2S: "RETR korte\r\n" S2P: "150 Data Follow\r\n" S2P: Accept dS2P: "En vagyok a korte." dS2P: Disconnect P2C: "HTTP/1.0 200 OK\r\n\ Content-Type: application/octet-stream\r\n\ Proxy-Connection: close\r\n\ \r\n\ En vagyok a korte." . StartInfo Tags bug9787 EndInfo # Download file from subdirectory, server uses 125 status to confirm data channel. C2P: "GET ftp://ftp.szerver.hu/almafa/korte HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /almafa/korte\r\n" S2P: "550 /almafa/korte: Not a directory\r\n" P2S: "CWD /almafa\r\n" S2P: "250 Requested file action okay, completed\r\n" P2S: "TYPE I\r\n" S2P: "200 Representation type is Binary\r\n" P2S: "PASV\r\n" PORT=1294 SERVER_DOTIP=string.replace(ZTS_SERVER_IP,".",",") S2P: Listen PORT S2P: "227 Entering Passive Mode (" %SERVER_DOTIP ",5,14)\r\n" P2S: "RETR korte\r\n" S2P: "125 Data Follow\r\n" S2P: Accept dS2P: "En vagyok a korte." dS2P: Disconnect P2C: "HTTP/1.0 200 OK\r\n\ Content-Type: application/octet-stream\r\n\ Proxy-Connection: close\r\n\ \r\n\ En vagyok a korte." . # Download non-existing file from non-existing directory. C2P: "GET ftp://ftp.szerver.hu/nihil/nonexists.txt HTTP/1.1\r\n\ Host: ftp.szerver.hu\r\n\ \r\n" S2P: "220 Szia\r\n" P2S: "USER anonymous\r\n" S2P: "331 Send your password\r\n" P2S: "PASS ftp@\r\n" S2P: "230 Anonymous access granted\r\n" P2S: "CWD /nihil/nonexists.txt\r\n" S2P: "550 /nihil/nonexists.txt: No such file or directory\r\n" P2S: "CWD /nihil\r\n" S2P: "550 /nihil: No such file or directory\r\n" P2C: "HTTP/1.0 500 Error encountered\r\n\ Proxy-Connection: close\r\n\ Content-Type: text/html\r\n\ \r\n" $NIHIL "\n" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/host-rewrite.tests000066400000000000000000000022311172670260400275220ustar00rootroot00000000000000 StartGlobalInfo Tags bug5144 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.transparent_mode = FALSE self.default_port = server_port self.target_port_range = "80-2000" EndPolicy C2P: "GET http://" %ZTS_SERVER_IP "/var/www/index.html HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP "\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . C2P: "GET http://" %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "/var/www/index.html HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: " %ZTS_SERVER_IP ":" %ZTS_SERVER_PORT "\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" . zorp-3.9.5/tests/functional/http/protocol/non-transparent/info000066400000000000000000000000251172670260400246570ustar00rootroot00000000000000Tags: nontransparent zorp-3.9.5/tests/functional/http/protocol/parent-proxy.tests000066400000000000000000000027501172670260400244130ustar00rootroot00000000000000 StartGlobalInfo Tags bug5144 Min-Version 3.0 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.parent_proxy = '1' EndPolicy # 5144 # 1.0 request, 1.0 response; Proxy-Connection header present -> persistent ##INFO## ##Tags: bug5144 ## C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET http://www/ HTTP/1.0\r\n\ Host: www\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: close\r\n\ \r\n" P2S: "GET http://www/ HTTP/1.0\r\n\ Host: www\r\n\ Proxy-Connection: close\r\n\ \r\n" S2P: Disconnect P2C: Disconnect . # 5144 # 1.0 request, 1.0 response; Connection header present -> persistent C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET http://www/ HTTP/1.0\r\n\ Host: www\r\n\ Proxy-Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: close\r\n\ \r\n" P2S: "GET http://www/ HTTP/1.0\r\n\ Host: www\r\n\ Proxy-Connection: close\r\n\ \r\n" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/persistent-mode.tests000066400000000000000000000044261172670260400250670ustar00rootroot00000000000000StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy # 1.0 request, no connection header -> non-persistent C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" S2P: Disconnect P2C: Disconnect . # 1.0 request, 1.0 response; connection header present -> persistent C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: close\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: close\r\n\ \r\n" S2P: Disconnect P2C: Disconnect . # 1.0 request with connection keep-alive # 1.1 response without header; -> non-persistent, with an explicit Connection header added C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Connection: keep-alive\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" S2P: Disconnect P2C: Disconnect . StartInfo Tags bug4096 EndInfo # 4096 C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ \r\n\ entitas resze\r\n\ \r\n" S2P: Disconnect P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ \r\n\ entitas resze\r\n\ \r\n" P2C: Disconnect . StartInfo Tags bug6444 EndInfo # HTTP/1.1 reconnect when the server drops connection while we are waiting # for the client C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 17\r\n\ \r\n\ entitas resze\r\n\ \r\n" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 17\r\n\ \r\n\ entitas resze\r\n\ \r\n" C2P: "GET / HTTP/1.1\r\n" A=time.sleep(1) S2P: Disconnect C2P: "Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/proto-upgrade.tests000066400000000000000000000007231172670260400245310ustar00rootroot00000000000000StartGlobalInfo Tags bug9423 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.keep_persistent=TRUE EndPolicy C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ \r\n\ 12345" S2P: Disconnect P2C: "HTTP/1.1 200 OK\r\n\ Connection: keep-alive\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 5\r\n\ 12345\r\n\ 0\r\n\r\n" . zorp-3.9.5/tests/functional/http/protocol/rerequest.tests000066400000000000000000000040611172670260400237570ustar00rootroot00000000000000 StartGlobalInfo Tags bug9808 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.rerequest_attempts = 3 self.timeout_response = 3000 EndPolicy C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" A=time.sleep(5) S2P: Disconnect P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . C2P: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" P2S: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" A=time.sleep(5) S2P: Disconnect P2S: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . C2P: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" P2S: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ \r\n\ 12345" C2P: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET / HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . StartInfo Tags qqq EndInfo C2P: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ a\r\n\ 1234567890\r\n\ 0\r\n\r\n" P2S: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" A=time.sleep(5) S2P: Disconnect P2S: "POST / HTTP/1.1\r\n\ Host: www\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . zorp-3.9.5/tests/functional/http/protocol/short-status-line.tests000066400000000000000000000006771172670260400253560ustar00rootroot00000000000000StartGlobalInfo Tags bug8292 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.require_host_header = FALSE self.permit_http09_responses = TRUE EndPolicy #simple HTTP/0.9 request and response C2P: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "ent" S2P: Disconnect P2C: "ent" P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/small-web-latency.tests000066400000000000000000000007451172670260400252650ustar00rootroot00000000000000StartGlobalInfo Tags D-01817 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) EndPolicy C2P: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 7\r\n\ \r\n\ a" A=time.sleep(5) S2P: "a" A=time.sleep(5) S2P: "a" A=time.sleep(5) S2P: "a" A=time.sleep(5) S2P: "a\r\n" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 7\r\n\ \r\n\ aaaaa\r\n" . zorp-3.9.5/tests/functional/http/protocol/smuggle.tests000066400000000000000000000011451172670260400234030ustar00rootroot00000000000000StartGlobalInfo Tags bug7034 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy # some duplicated headers are elminated C2P: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Host: www2\r\n\ Accept: 1\r\n\ Accept: 2\r\n\ \r\n" P2S: "GET / HTTP/1.0\r\n\ Host: www\r\n\ Accept: 1\r\n\ Accept: 2\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Content-Length: 15\r\n\ Age: 1\r\n\ Age: 2\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Age: 1\r\n\ Age: 2\r\n\ \r\n\ 12345" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/http/protocol/strict-header-check.tests000066400000000000000000000062101172670260400255470ustar00rootroot00000000000000StartGlobalInfo Tags bug11836 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.strict_header_checking=TRUE EndPolicy # Invalid character C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: invalid character: ? * .\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ \r\n" . # Unknown header C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ UnknownHeader: pointless value\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ \r\n" . # Value too long C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: \ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 012345679012345\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ \r\n" . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.strict_header_checking=FALSE EndPolicy # Invalid character C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: invalid character: ? * .\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: invalid character: ? * .\r\n\ \r\n" . # Unknown header C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ UnknownHeader: pointless value\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ UnknownHeader: pointless value\r\n\ \r\n" . # Value too long C2P: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: \ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 012345679012345\r\n\ \r\n" P2S: "GET /pub/var/index.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ Via: \ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\ 012345679012345\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/protocol/url-encoding.tests000066400000000000000000000031761172670260400243340ustar00rootroot00000000000000StartGlobalInfo Tags urlrewrite EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy C2P: "GET /pub/var/index%s%s.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ \r\n" P2C: "HTTP/1.0 500 Error encountered" $NIHIL "\n" . C2P: "GET /pub/var/index%ab.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ \r\n" P2S: "GET /pub/var/index%AB.html HTTP/1.0\r\n\ Host: www.w3.org\r\n\ \r\n" . C2P: "GET /dict_search.php?L=ENG%3AHUN%3AEngHunDict&O=HUN&flash=&E=1&sid=39d326b3ed5258dfc0e695d6af882aae&in_form=1&W=provide&M=2&P=0&C=0&A=0&T=1&F=0 HTTP/1.0\r\n\ Host: szotar.sztaki.hu\r\n\ \r\n" P2S: "GET /dict_search.php?L=ENG%3AHUN%3AEngHunDict&O=HUN&flash=&E=1&sid=39d326b3ed5258dfc0e695d6af882aae&in_form=1&W=provide&M=2&P=0&C=0&A=0&T=1&F=0 HTTP/1.0\r\n\ Host: szotar.sztaki.hu\r\n\ \r\n" . StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.permit_unicode_url = TRUE self.transparent_mode = FALSE self.parent_proxy="some" EndPolicy C2P: "GET http://%73%61:%70%61%73%73@a.b/%63 HTTP/1.0\r\n\ Host: a.b\r\n\ \r\n" P2S: "GET http://sa:pass@a.b/c HTTP/1.0\r\n\ Host: a.b\r\n\ \r\n" . C2P: "GET http://latin2.hu/k%e9r%e9s.php?b%e9la HTTP/1.0\r\n\ Host: latin2.hu\r\n\ \r\n" P2S: "GET http://latin2.hu/k%E9r%E9s.php?b%E9la HTTP/1.0\r\n\ Host: latin2.hu\r\n\ \r\n" . C2P: "GET http://utf8.hu/k%u00e9r%u01e9s.php HTTP/1.0\r\n\ Host: utf8.hu\r\n\ \r\n" P2S: "GET http://utf8.hu/k%E9r%u01E9s.php HTTP/1.0\r\n\ Host: utf8.hu\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/transfer/000077500000000000000000000000001172670260400206365ustar00rootroot00000000000000zorp-3.9.5/tests/functional/http/transfer/body-length-limit.tests000066400000000000000000000033271172670260400252570ustar00rootroot00000000000000StartGlobalInfo Tags limits EndGlobalInfo # "Max length" scenarios StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.max_body_length = 10 self.max_chunk_length = 8 EndPolicy # explicit length C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 10\r\n\ \r\n\ 1234567890" . # chunked length C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 4\r\n\ 1234\r\n\ 8\r\n\ 56789012\r\n\ 0\r\n\r\n" P2C: "HTTP/1.1 200 OK\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 4\r\n\ 1234\r\n\ 6\r\n\ 567890\r\n\ 0\r\n\r\n" . # no length C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ \r\n\ 123456789012" P2C: "HTTP/1.0 200 OK\r\n\ \r\n\ 1234567890" . # max_chunk_length C2P: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 6\r\n\ \r\n\ haliho" P2S: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 6\r\n\ \r\n\ haliho" S2P: "HTTP/1.1 200 OK\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ a\r\n\ 1234567890\r\n\ 0\r\n" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . zorp-3.9.5/tests/functional/http/transfer/bug14351.tests000066400000000000000000000023351172670260400231000ustar00rootroot00000000000000StartGlobalInfo Tags bug14351 EndGlobalInfo StartPolicy def config(self): self.request_stack["*"] = (HTTP_STK_DATA, "/bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_ACCEPT\\\\nn\\\\nn'>&3; read alma <&3; /bin/echo -en '123456789012'") HttpProxy.config(self) self.timeout_request=30000 EndPolicy C2P: "GET /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: close\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" P2S: "GET /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: close\r\n\ Transfer-Encoding: chunked\r\n\ \r\nc\r\n123456789012\r\n0\r\n\r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" . StartPolicy def config(self): self.request_stack["*"] = (HTTP_STK_DATA, "/bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_REJECT\\\\nn\\\\nn'>&3; read alma <&3") HttpProxy.config(self) self.timeout_request=30000 EndPolicy C2P: "GET /reject.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: close\r\n\ Content-Length: 6\r\n\ \r\n\ reject\n" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . zorp-3.9.5/tests/functional/http/transfer/chunked-conversion.tests000066400000000000000000000034551172670260400255350ustar00rootroot00000000000000StartGlobalInfo Tags bug6233 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request_stack["POST"] = (HTTP_STK_DATA, "/bin/cat") self.response_stack["POST"] = (HTTP_STK_DATA, "/bin/cat") EndPolicy # fix length -> chunked conversion C2P: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 6\r\n\ \r\n\ haliho" P2S: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 6\r\n\ haliho\r\n\ 0\r\n\r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ Connection: close\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ a\r\n\ 1234567890\r\n\ 0\r\n\r\n" . # chunked -> chunked C2P: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 6\r\n\ haliho\r\n\ 0\r\n\r\n" P2S: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 6\r\n\ haliho\r\n\ 0\r\n\r\n" S2P: "HTTP/1.1 200 OK\r\n\ Transfer-Encoding: chunked\r\n\ Connection: close\r\n\ \r\n\ a\r\n\ 1234567890\r\n\ 0\r\n\r\n" P2C: "HTTP/1.1 200 OK\r\n\ Transfer-Encoding: chunked\r\n\ Connection: close\r\n\ \r\n\ a\r\n\ 1234567890\r\n\ 0\r\n\r\n" . # EOF terminated -> chunked C2P: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 6\r\n\ haliho\r\n\ 0\r\n\r\n" P2S: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 6\r\n\ haliho\r\n\ 0\r\n\r\n" S2P: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ \r\n\ 1234567890" S2P: Disconnect P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ a\r\n\ 1234567890\r\n\ 0\r\n\r\n" . zorp-3.9.5/tests/functional/http/transfer/chunked-encoding.tests000066400000000000000000000032161172670260400251310ustar00rootroot00000000000000StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 EndPolicy #SaSa test. C2P: "GET /var/www/index.html HTTP/1.1\r\n\ TE: deflate, gzip, chunked, identity, trailers\r\n\ Host: www.net.org\r\n\ Connection: close\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ TE: deflate, gzip, chunked, identity, trailers\r\n\ Host: www.net.org\r\n\ Connection: close\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Content-Type: text/plain\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 0\r\n\ \r\n" P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Content-Type: text/plain\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 0\r\n\ \r\n" . # Chunked transfer coding C2P: "GET /var/www/index.html HTTP/1.1\r\n\ TE: chunked\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ TE: chunked\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Type: text/plain\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 7\r\n\ chunked\r\n\ 0\r\n\ \r\n" P2C: "HTTP/1.1 200 OK\r\n\ Content-Type: text/plain\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 7\r\n\ chunked\r\n\ 0\r\n\ \r\n" . # ignored Content-Length C2P: "GET /var/www/index.html HTTP/1.1\r\n\ TE: chunked\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ TE: chunked\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Type: text/plain\r\n\ Transfer-Encoding: chunked\r\n\ Content-Length: 12\r\n\ \r\n\ 4\r\n\ 1234\r\n\ 8\r\n\ 56789012\r\n\ 0\r\n\ \r\n" P2C: "HTTP/1.1 200 OK\r\n\ Content-Type: text/plain\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 4\r\n\ 1234\r\n\ 8\r\n\ 56789012\r\n\ 0\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/transfer/http-1.1-0.9-fallback-chunking.tests000066400000000000000000000017541172670260400270520ustar00rootroot00000000000000 StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.require_host_header = FALSE self.permit_http09_responses = TRUE # no real stacking, but let the traffic through an external program self.response_stack["*"] = (HTTP_STK_DATA, "/bin/cat") EndPolicy StartInfo Tags bug4567 Min-Version 3.0 EndInfo #HTTP 1.1 request and HTTP 0.9 response C2P: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" P2S: "GET /foo.html HTTP/1.1\r\n\ Host: www\r\n\ \r\n" S2P: "This is a simple, but long response without headers. The proxy should not add chunking here.\r\n\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" P2C: "This is a simple, but long response without headers. The proxy should not add chunking here.\r\n\ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n" S2P: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/http/transfer/info000066400000000000000000000000171172670260400215120ustar00rootroot00000000000000Tags: transfer zorp-3.9.5/tests/functional/http/transfer/mime-stacked-content-length.tests000066400000000000000000000034301172670260400272140ustar00rootroot00000000000000StartGlobalInfo Tags bug9529 Min-Version 3.0 EndGlobalInfo # FIXME: We need a testcase when http send a content length hint and receiving it back # #StartPolicy # def config(self): # HttpProxy.config(self) # self.timeout_request=30000 # self.response_stack["GET"] = (HTTP_STK_MIME, "/bin/cat >/dev/null; /bin/echo -en '0 SETCONTENTHINT\\\\nn[]Content-Length\\\\nn61\\\\nn\\\\nnSETVERDICT\\\\nn[]Verdict\\\\nnZ_ACCEPT\\\\nn\\\\nn'>&3; read alma <&3; /bin/echo -en 'Content-Type: text/plain\\\\rr\\\\nnContent-Length: 12\\\\rr\\\\nn\\\\rr\\\\nn123456789012'") #EndPolicy # # #C2P: "GET /var/www/index.html HTTP/1.1\r\n\ #Host: www.net.org\r\n\ #\r\n" #P2S: "GET /var/www/index.html HTTP/1.1\r\n\ #Host: www.net.org\r\n\ #\r\n" #S2P: "HTTP/1.1 200 OK\r\n\ #Content-Type: text/plain\r\n\ #Content-Length: 12\r\n\ #\r\n\ #123456789012" #P2C: "HTTP/1.1 200 OK\r\n\ #Content-Type: text/plain\r\n\ #Content-Length: 12\r\n\ #\r\n\ #123456789012" #. StartPolicy def config(self): HttpProxy.config(self) self.response_stack["*"] = (HTTP_STK_MIME, "/bin/cat >/dev/null; /bin/echo -en '0 SETCONTENTHINT\\\\nn[]Content-Length\\\\nn61\\\\nn\\\\nnSETVERDICT\\\\nn[]Verdict\\\\nnZ_ACCEPT\\\\nn\\\\nn'>&3; read alma <&3; /bin/echo -en 'Content-Type: text/plain\\\\rr\\\\nnContent-Length: 122\\\\rr\\\\nn\\\\rr\\\\nn123456789012'") EndPolicy C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Type: text/plain\r\n\ Content-Length: 122\r\n\ \r\n\ 123456789012345678901234567890123456789012345678901234567890\r\n\ 123456789012345678901234567890123456789012345678901234567890" P2C: "HTTP/1.1 200 OK\r\n\ Content-Type: text/plain\r\n\ Content-Length: 12\r\n\ \r\n\ 123456789012" . zorp-3.9.5/tests/functional/http/transfer/negative-content-length.tests000066400000000000000000000007261172670260400264600ustar00rootroot00000000000000StartGlobalInfo Tags bug8820 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) EndPolicy # fix length -> chunked conversion C2P: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" P2S: "GET /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: -2\r\n\ \r\n\ 1234567890" S2P: Disconnect P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ \r\n\ 1234567890" . zorp-3.9.5/tests/functional/http/transfer/no-content-length-hint.tests000066400000000000000000000065511172670260400262340ustar00rootroot00000000000000StartGlobalInfo Tags bug18612 EndGlobalInfo StartPolicy def config(self): self.response_stack["*"] = (HTTP_STK_DATA, "read;/bin/echo -en '0 RESULT\\\\nn[1]Handshake-Id\\\\nn0\\\\nn[2]Status\\\\nnOK\\\\nn\\\\nn0 SETVERDICT\\\\nn[8]Description\\\\nnAccepted\\\\nn[3]Details\\\\nnn/a\\\\nn[8]Verdict\\\\nnZ_ACCEPT\\\\nn\\\\nn'>&3; read alma <&3; /bin/echo -en '124'") HttpProxy.config(self) self.timeout_request=30000 EndPolicy # close, HTTP/1.0 C2P: "GET /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: close\r\n\r\n" P2S: "GET /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: close\r\n\r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ \r\n\ 124" . # close, HTTP/1.1 C2P: "GET /accept.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: close\r\n\r\n" P2S: "GET /accept.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: close\r\n\r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: close\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Connection: close\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 3\r\n\ 124\r\n\ 0\r\n\ \r\n" . # keepalive + GET, HTTP/1.0 C2P: "GET /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\r\n" P2S: "GET /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Connection: close\r\n\ \r\n\ 124" . # keepalive + GET, HTTP/1.1 C2P: "GET /accept.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\r\n" P2S: "GET /accept.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Connection: keep-alive\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 3\r\n\ 124\r\n\ 0\r\n\ \r\n" . StartPolicy def config(self): self.request_stack["*"] = (HTTP_STK_DATA, "read;/bin/echo -en '0 RESULT\\\\nn[1]Handshake-Id\\\\nn0\\\\nn[2]Status\\\\nnOK\\\\nn\\\\nn0 SETVERDICT\\\\nn[8]Description\\\\nnAccepted\\\\nn[3]Details\\\\nnn/a\\\\nn[8]Verdict\\\\nnZ_ACCEPT\\\\nn\\\\nn'>&3; read alma <&3; /bin/echo -en '124'") HttpProxy.config(self) self.timeout_request=30000 self.request["*"] = (HTTP_REQ_ACCEPT,) EndPolicy # keepalive + PUT, HTTP/1.0 C2P: "PUT /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Content-Length: 6\r\n\ Connection: keep-alive\r\n\ \r\n\ 123456" P2S: "PUT /accept.html HTTP/1.0\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 3\r\n\ 124\r\n\ 0\r\n\ \r\n" S2P: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.0 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" . # keepalive + PUT, HTTP/1.1 C2P: "PUT /accept.html HTTP/1.1\r\n\ Host: test1\r\n\ Content-Length: 6\r\n\ Connection: keep-alive\r\n\ \r\n\ 123456" P2S: "PUT /accept.html HTTP/1.1\r\n\ Host: test1\r\n\ Connection: keep-alive\r\n\ Transfer-Encoding: chunked\r\n\ \r\n\ 3\r\n\ 124\r\n\ 0\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 5\r\n\ Connection: keep-alive\r\n\ \r\n\ 12345" . zorp-3.9.5/tests/functional/http/transfer/upload-error-page.tests000066400000000000000000000021211172670260400252430ustar00rootroot00000000000000StartGlobalInfo Tags bug8533 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request_stack["POST"] = (HTTP_STK_DATA, "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_REJECT\\\\nn[]Description\\\\nn Nem nyert\\\\nn\\\\nn'>&3; read alma <&3") self.request_stack["HEAD"] = (HTTP_STK_DATA, "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_REJECT\\\\nn[]Description\\\\nn Nem nyert\\\\nn\\\\nn'>&3; read alma <&3") EndPolicy # When uploading is rejected send an errorpage to the client C2P: "POST /var/www/eicar.com HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 68\r\n\ \r\n\ X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" P2C: "HTTP/1.0 500 Error encountered\r\n" $NIHIL "\n" . C2P: "HEAD /var/www/eicar.com HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 68\r\n\ \r\n\ X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" P2C: "HTTP/1.0 500 Error encountered\r\n\ Connection: close\r\n\ Content-Type: text/html\r\n\ \r\n" . zorp-3.9.5/tests/functional/http/transfer/zero-sized-post.tests000066400000000000000000000022271172670260400250030ustar00rootroot00000000000000StartGlobalInfo Tags bug11057 EndGlobalInfo StartPolicy def config(self): HttpProxy.config(self) self.timeout_request=30000 self.request_stack["POST"] = (HTTP_STK_DATA, "/bin/cat") self.request["PUT"] = (HTTP_REQ_ACCEPT) EndPolicy # handle zero sized POST requests properly (stacking is requested) C2P: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 0\r\n\ \r\n" P2S: "POST /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 0\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ Connection: close\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ Connection: close\r\n\ \r\n\ 1234567890" . # handle zero sized PUT requests properly (stacking is not requested) C2P: "PUT /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 0\r\n\ \r\n" P2S: "PUT /var/www/index.html HTTP/1.1\r\n\ Host: www.net.org\r\n\ Content-Length: 0\r\n\ \r\n" S2P: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ Connection: close\r\n\ \r\n\ 1234567890" P2C: "HTTP/1.1 200 OK\r\n\ Content-Length: 10\r\n\ Connection: close\r\n\ \r\n\ 1234567890" . zorp-3.9.5/tests/functional/plug/000077500000000000000000000000001172670260400170025ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/attr/000077500000000000000000000000001172670260400177545ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/attr/cases/000077500000000000000000000000001172670260400210525ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/attr/cases/testcases000066400000000000000000000014461172670260400230000ustar00rootroot00000000000000 StartPolicy def config(self): self.copy_to_server=FALSE EndPolicy C2P: "Nem_megy_at" S2P: "Atmegy" P2C: "Atmegy" . StartPolicy def config(self): self.copy_to_client=FALSE EndPolicy C2P: "Atmegy" P2S: "Atmegy" S2P: "Nem_megy_at" . StartPolicy def config(self): self.timeout = 2000 EndPolicy C2P: "Atmegy" P2S: "Atmegy" A= time.sleep(3) P2C: Disconnect P2S: Disconnect . StartPolicy def config(self): self.packet_stats_interval_packet = 5 def packetStats(self, client_bytes, client_pkts, server_bytes, server_pkts): return Z_ACCEPT EndPolicy C2P: "C1" P2S: "C1" S2P: "S1" P2C: "S1" C2P: "C2" P2S: "C2" S2P: "S2" P2C: "S2" C2P: "C3" P2S: "C3" S2P: "S3" P2C: "S3" C2P: "C4" P2S: "C4" S2P: "S4" P2C: "S4" C2P: "C5" P2S: "C5" S2P: "S5" P2C: "S5" C2P: "C6" P2S: "C6" S2P: "S6" P2C: "S6" . zorp-3.9.5/tests/functional/plug/attr/cases/timeout.tests000066400000000000000000000004451172670260400236270ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): self.timeout = 3000 EndPolicy C2P: "1" A=time.sleep(6) C2P: "2" P2S: "1" P2S: Disconnect P2C: Disconnect . S2P: "1" A=time.sleep(6) S2P: "2" P2C: "1" P2C: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/plug/attr/config000066400000000000000000000000001172670260400211320ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/func/000077500000000000000000000000001172670260400177355ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/func/cases/000077500000000000000000000000001172670260400210335ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/func/cases/testcases000066400000000000000000000005621172670260400227570ustar00rootroot00000000000000 #First case. Proxy working? StartPolicy def config(self): pass EndPolicy C2P: "Haho" P2S: "Haho" S2P: "Hello" P2C: "Hello" . #Testing bidirectional C2P: "One" S2P: "Two" P2C: "Two" P2S: "One" . #Testing forced-one-way StartPolicy def config(self): PlugProxy.config(self) self.copy_to_server=FALSE EndPolicy C2P: "Nem_megy_at" S2P: "Atmegy" P2C: "Atmegy" .zorp-3.9.5/tests/functional/plug/func/config000066400000000000000000000000001172670260400211130ustar00rootroot00000000000000zorp-3.9.5/tests/functional/plug/info000066400000000000000000000000261172670260400176560ustar00rootroot00000000000000Base-Class: PlugProxy zorp-3.9.5/tests/functional/plug/spec000066400000000000000000000000521172670260400176540ustar00rootroot00000000000000StartSpec Proxy PlugProxy EndSpec zorp-3.9.5/tests/functional/pop3/000077500000000000000000000000001172670260400167145ustar00rootroot00000000000000zorp-3.9.5/tests/functional/pop3/attr/000077500000000000000000000000001172670260400176665ustar00rootroot00000000000000zorp-3.9.5/tests/functional/pop3/attr/cases/000077500000000000000000000000001172670260400207645ustar00rootroot00000000000000zorp-3.9.5/tests/functional/pop3/attr/cases/testcases000066400000000000000000000142171172670260400227120ustar00rootroot00000000000000StartPolicy def config(self): self.request["*"] = POP3_REQ_ACCEPT self.timeout = 2000 self.request["STAT"] = (POP3_REQ_POLICY, self.test) self.request["LIST"] = (POP3_REQ_POLICY, self.test2) def test(self,command): print self.username print type(self.username) if self.username == "mrose": self.request_command = "LIST" return POP3_REQ_ACCEPT def test2(self,command): print self.password print type(self.password) if self.password == "secret": self.request_param = "5" return POP3_REQ_ACCEPT EndPolicy #Timeout check moved to timeout.tests S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "LIST\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP barmi c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP barmi c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK barmi's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK barmi's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER barmi\r\n" P2S: "USER barmi\r\n" S2P: "+OK barmi is a real hoopy frood\r\n" P2C: "+OK barmi is a real hoopy frood\r\n" C2P: "PASS secret\r\n" P2S: "PASS secret\r\n" S2P: "+OK barmi's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK barmi's maildrop has 2 messages (320 octets)\r\n" C2P: "LIST 1\r\n" P2S: "LIST 5\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER barmi\r\n" P2S: "USER barmi\r\n" S2P: "+OK barmi is a real hoopy frood\r\n" P2C: "+OK barmi is a real hoopy frood\r\n" C2P: "PASS secret1\r\n" P2S: "PASS secret1\r\n" S2P: "+OK barmi's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK barmi's maildrop has 2 messages (320 octets)\r\n" C2P: "LIST 1\r\n" P2S: "LIST 1\r\n" . StartPolicy def config(self): self.request["*"] = POP3_REQ_ACCEPT self.max_request_line_length = 10 self.max_response_line_length = 15 EndPolicy S2P: "+OK POP3 server\r\n" P2C: "+OK POP3 server\r\n" C2P: "USER barmi\r\n" P2S: "USER barmi\r\n" S2P: "+OK bar is a rea\r\n" P2C: "-ERR Error in protocol\r\n" P2C: Disconnect P2S: Disconnect . S2P: "+OK POP3 server\r\n" P2C: "+OK POP3 server\r\n" C2P: "USER barmi\r\n" P2S: "USER barmi\r\n" S2P: "+OK bar is a re\r\n" P2C: "+OK bar is a re\r\n" C2P: "PASS secret\r\n" P2S: Disconnect P2C: "-ERR Invalid co" P2C: Disconnect . StartPolicy def config(self): self.request["*"] = POP3_REQ_ACCEPT self.max_username_length = 5 self.max_password_length = 5 EndPolicy S2P: "+OK POP3 serv\r\n" P2C: "+OK POP3 serv\r\n" C2P: "USER barmi\r\n" P2S: "USER barmi\r\n" S2P: "+OK bar is a \r\n" P2C: "+OK bar is a \r\n" C2P: "PASS 12345\r\n" P2S: "PASS 12345\r\n" . S2P: "+OK POP3 serv\r\n" P2C: "+OK POP3 serv\r\n" C2P: "USER barmi1\r\n" P2C: "-ERR Invalid command.\r\n" . S2P: "+OK POP3 serv\r\n" P2C: "+OK POP3 serv\r\n" C2P: "USER barmi\r\n" P2S: "USER barmi\r\n" S2P: "+OK bar is a \r\n" P2C: "+OK bar is a \r\n" C2P: "PASS 123456\r\n" P2C: "-ERR Invalid command.\r\n" . StartPolicy def config(self): self.request["*"] = POP3_REQ_ACCEPT self.request["STAT"] = (POP3_REQ_POLICY, self.semmi, self.test) self.request["LIST"] = (POP3_REQ_POLICY, self.semmi, self.test2) def semmi(self, command): return POP3_REQ_ACCEPT def test(self,command): if self.response_value == "+OK": self.response_value = "-ERR" return POP3_RSP_ACCEPT def test2(self,command): if self.response_param == "valami": self.response_param = "valami_mas" return POP3_RSP_ACCEPT EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK fittyerfutty\r\n" P2C: "-ERR fittyerfutty\r\n" C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "-ERR valami\r\n" P2C: "-ERR valami_mas\r\n" . StartPolicy def config(self): self.request["*"] = POP3_REQ_ACCEPT self.request["STAT"] = (POP3_REQ_POLICY, self.test) def test(self,command): print self.session_timestamp print type(self.session_timestamp) if self.session_timestamp == "<1896.697170952@dbc.mtview.ca.us>": return POP3_RSP_ACCEPT else: self.response_param = "not allowed" return POP3_RSP_REJECT EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" . S2P: "+OK POP3 server ready \r\n" P2C: "+OK POP3 server ready \r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2C: "-ERR not allowed\r\n" . S2P: "+OK POP3 server ready \r\n" P2C: "+OK POP3 server ready \r\n" C2P: "HUBB valami\r\n" P2S: Disconnect P2C: "-ERR Invalid command.\r\n" P2C: Disconnect . StartPolicy def config(self): self.request["GREETING"] = POP3_REQ_ACCEPT self.permit_unknown_command = TRUE self.request["HUBB"] = POP3_REQ_ACCEPT EndPolicy S2P: "+OK POP3 server ready \r\n" P2C: "+OK POP3 server ready \r\n" C2P: "HUBB valami\r\n" P2S: "HUBB valami\r\n" S2P: "+OK barmi\r\n" P2C: "+OK barmi\r\n" . zorp-3.9.5/tests/functional/pop3/attr/cases/timeout.tests000066400000000000000000000022521172670260400235370ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): self.request["*"] = POP3_REQ_ACCEPT self.timeout = 3000 self.request["STAT"] = (POP3_REQ_POLICY, self.test) self.request["LIST"] = (POP3_REQ_POLICY, self.test2) def test(self,command): print self.username print type(self.username) if self.username == "mrose": self.request_command = "LIST" return POP3_REQ_ACCEPT def test2(self,command): print self.password print type(self.password) if self.password == "secret": self.request_param = "5" return POP3_REQ_ACCEPT EndPolicy S2P: "+OK POP3 server ready" A= time.sleep(6) S2P: " <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "-ERR Error in protocol\r\n" P2C: Disconnect . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP" A= time.sleep(6) C2P: " mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2C: "-ERR Invalid command.\r\n" P2S: Disconnect P2C: Disconnect . zorp-3.9.5/tests/functional/pop3/attr/info000066400000000000000000000000161172670260400205410ustar00rootroot00000000000000Disabled: yes zorp-3.9.5/tests/functional/pop3/func/000077500000000000000000000000001172670260400176475ustar00rootroot00000000000000zorp-3.9.5/tests/functional/pop3/func/cases/000077500000000000000000000000001172670260400207455ustar00rootroot00000000000000zorp-3.9.5/tests/functional/pop3/func/cases/testcases000066400000000000000000000530261172670260400226740ustar00rootroot00000000000000#Some valid scenarios StartPolicy def config(self): Pop3Proxy.config(self) EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "+OK 2 messages (320 octets)\r\n" \ "1 120\r\n" \ "2 200\r\n" \ ".\r\n" P2C: "+OK 2 messages (320 octets)\r\n" \ "1 120\r\n" \ "2 200\r\n" \ ".\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK 120 octets\r\n" \ "\r\n" \ ".\r\n" P2C: "+OK 120 octets\r\n" \ "\r\n" \ ".\r\n" C2P: "DELE 1\r\n" P2S: "DELE 1\r\n" S2P: "+OK message 1 deleted\r\n" P2C: "+OK message 1 deleted\r\n" C2P: "RETR 2\r\n" P2S: "RETR 2\r\n" S2P: "+OK 200 octets\r\n" \ "\r\n" \ ".\r\n" P2C: "+OK 200 octets\r\n" \ "\r\n" \ ".\r\n" C2P: "DELE 2\r\n" P2S: "DELE 2\r\n" S2P: "+OK message 2 deleted\r\n" P2C: "+OK message 2 deleted\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "+OK dewey POP3 server signing off (maildrop empty)\r\n" P2C: "+OK dewey POP3 server signing off (maildrop empty)\r\n" C2P: Disconnect P2S: Disconnect . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "+OK all right\r\n" P2C: "+OK all right\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK 12 octets\r\n" \ "123456789012\r\n.\r\n" P2C: "+OK 12 octets\r\n" \ "123456789012\r\n.\r\n" C2P: "DELE 1\r\n" P2S: "DELE 1\r\n" S2P: "+OK message 1 deleted\r\n" P2C: "+OK message 1 deleted\r\n" C2P: "RSET\r\n" P2S: "RSET\r\n" S2P: "+OK maildrop has 2 messages (320 octets)\r\n" P2C: "+OK maildrop has 2 messages (320 octets)\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "+OK dewey POP3 server signing off\r\n" P2C: "+OK dewey POP3 server signing off\r\n" C2P: Disconnect P2S: Disconnect . #TRANSACTION commands in AUTH state S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "STAT\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "LIST\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "RETR 1\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "DELE 1\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "NOOP\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "RSET\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "TOP 1 1\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "UIDL\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.69710952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.69710952@dbc.mtview.ca.us>\r\n" C2P: "USER valami\r\n" P2S: "USER valami\r\n" S2P: "-ERR never heard of mailbox name\r\n" P2C: "-ERR never heard of mailbox name\r\n" C2P: "PASS barki\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.69710952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.69710952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barki\r\n" P2S: "PASS barki\r\n" S2P: "-ERR invalid password\r\n" P2C: "-ERR invalid password\r\n" C2P: "TOP 1 1\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "-ERR permission denied\r\n" P2C: "-ERR permission denied\r\n" C2P: "RSET\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.69710952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.69710952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "PASS barmi\r\n" P2C: "-ERR " $NIHIL "\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" . #STAT S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "STAT 2\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" . #LIST S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "+OK 2 messages (320 octets)\r\n" \ "1 120\r\n" \ "2 200\r\n" \ ".\r\n" P2C: "+OK 2 messages (320 octets)\r\n" \ "1 120\r\n" \ "2 200\r\n" \ ".\r\n" C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "+OK 2 messages\r\n" \ "1 120\r\n" \ "2 200\r\n" \ ".\r\n" P2C: "+OK 2 messages\r\n" \ "1 120\r\n" \ "2 200\r\n" \ ".\r\n" C2P: "LIST 2\r\n" P2S: "LIST 2\r\n" S2P: "+OK 2 200\r\n" P2C: "+OK 2 200\r\n" C2P: "LIST 3\r\n" P2S: "LIST 3\r\n" S2P: "-ERR no such message\r\n" P2C: "-ERR no such message\r\n" . # RETR #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "USER valaki\r\n" #P2S: "USER valaki\r\n" #S2P: "+OK name is a valid mailbox\r\n" #P2C: "+OK name is a valid mailbox\r\n" #C2P: "PASS barmi\r\n" #P2S: "PASS barmi\r\n" #S2P: "+OK maildrop locked and ready\r\n" #P2C: "+OK maildrop locked and ready\r\n" #C2P: "RETR 1\r\n" #P2S: "RETR 1\r\n" #S2P: "+OK 120 octets follows\r\n" \ #"\r\n" \ #".\r\n" #P2C: "+OK 120 octets follows\r\n" \ #"\r\n" \ #".\r\n" # #C2P: "RETR 5\r\n" #P2S: "RETR 5\r\n" #S2P: "-ERR no such message\r\n" #P2C: "-ERR no such message\r\n" # #C2P: "RETR 2\r\n" #P2S: "RETR 2\r\n" #S2P: "+OK 5 octets\r\n" \ #"123456789\r\n.\r\n" #P2C: "-ERR " $NIHIL "\r\n" #. S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "RETR\r\n" P2C: "-ERR " $NIHIL "\r\n" . #DELE S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "DELE 1\r\n" P2S: "DELE 1\r\n" S2P: "+OK message deleted\r\n" P2C: "+OK message deleted\r\n" C2P: "DELE 2\r\n" P2S: "DELE 2\r\n" S2P: "-ERR message 2 already deleted\r\n" P2C: "-ERR message 2 already deleted\r\n" C2P: "DELE\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "DELE 0\r\n" P2C: "-ERR " $NIHIL "\r\n" . #RSET S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "RSET\r\n" P2S: "RSET\r\n" S2P: "+OK maildrop has 2 messages\r\n" P2C: "+OK maildrop has 2 messages\r\n" C2P: "RSET 2\r\n" P2S: "RSET\r\n" . #NOOP S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "+OK all right\r\n" P2C: "+OK all right\r\n" C2P: "NOOP 2\r\n" P2S: "NOOP\r\n" . #TOP S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "TOP 1 30\r\n" P2S: "TOP 1 30\r\n" S2P: "+OK top of message follows\r\n" \ "\r\n" \ "\r\n" \ "\r\n" \ ".\r\n" \ "\r\n.\r\n" P2C: "+OK top of message follows\r\n" \ "\r\n" \ "\r\n" \ "\r\n" \ ".\r\n" \ "\r\n.\r\n" C2P: "TOP 100 3\r\n" P2S: "TOP 100 3\r\n" S2P: "-ERR no such message\r\n" P2C: "-ERR no such message\r\n" C2P: "TOP 1\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "TOP\r\n" P2C: "-ERR " $NIHIL "\r\n" . #UIDL S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "UIDL\r\n" P2S: "UIDL\r\n" S2P: "+OK unique-id listing follows\r\n" \ "1 whqtswO00WBw418f9t5JxYwZ\r\n" \ "2 QhdPYR:00WBw1Ph7x7\r\n" \ ".\r\n" P2C: "+OK unique-id listing follows\r\n" \ "1 whqtswO00WBw418f9t5JxYwZ\r\n" \ "2 QhdPYR:00WBw1Ph7x7\r\n" \ ".\r\n" C2P: "UIDL 2\r\n" P2S: "UIDL 2\r\n" S2P: "+OK 2 QhdPYR:00WBw1Ph7x7\r\n" P2C: "+OK 2 QhdPYR:00WBw1Ph7x7\r\n" C2P: "UIDL 3\r\n" P2S: "UIDL 3\r\n" S2P: "-ERR no such message\r\n" P2C: "-ERR no such message\r\n" C2P: "UIDL valami\r\n" P2C: "-ERR " $NIHIL "\r\n" . # Response test S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK a"x512 "\r\n.\r\n" P2C: "-ERR" $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK bumburnyak\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "LIST 2\r\n" P2S: "LIST 2\r\n" S2P: "+OK2 320\r\n" P2C: "-ERR " $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "-ok all right\r\n" P2C: "-ERR" $NIHIL "\r\n" . S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "-HRR xsrfsklm\r\n" P2C: "-ERR" $NIHIL "\r\n" . # empty multiline response S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has no messages (0 octets)\r\n" P2C: "+OK mrose's maildrop has no messages (0 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 0 0\r\n" P2C: "+OK 0 0\r\n" C2P: "LIST\r\n" P2S: "LIST\r\n" S2P: "+OK 0 messages\r\n" \ ".\r\n" P2C: "+OK 0 messages\r\n" \ ".\r\n" . # Disconnect test #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" #P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" #S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" #P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" #C2P: "RETR 1\r\n" #P2S: "RETR 1\r\n" #S2P: "+OK 1024 byte coming\r\n" ##P2C: "+OK 1024 byte coming\r\n" #S2P: "abc" #S2P: Disconnect #. # XSENDER test StartPolicy def config(self): Pop3Proxy.config(self) self.request["XSENDER"] = POP3_REQ_ACCEPT EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "XSENDER 1\r\n" P2S: "XSENDER 1\r\n" S2P: "-ERR Unknown command\r\n" P2C: "-ERR Unknown command\r\n" . # Multiline state machine S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "UIDL\r\n" P2S: "UIDL\r\n" S2P: "+OK unique-id listing follows\r\n" \ "1 whqtswO00WBw418f9t5JxYwZ\r\n" \ "2 QhdPYR:00WBw1Ph7x7\r\n" \ ".\r\n" P2C: "+OK unique-id listing follows\r\n" \ "1 whqtswO00WBw418f9t5JxYwZ\r\n" \ "2 QhdPYR:00WBw1Ph7x7\r\n" \ ".\r\n" C2P: "XSENDER 1\r\n" P2S: "XSENDER 1\r\n" S2P: "+OK something\r\n" P2C: "+OK something\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "+OK all right\r\n" P2C: "+OK all right\r\n" . #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" #P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" #S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" #P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" #C2P: "RETR 1\r\n" #P2S: "RETR 1\r\n" #S2P: "+OK 1024 byte coming\r\n" ##P2C: "+OK 1024 byte coming\r\n" #S2P: "abc\r" #S2P: Disconnect #. #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" #P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" #S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" #P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" #C2P: "RETR 1\r\n" #P2S: "RETR 1\r\n" #S2P: "+OK 1024 byte coming\r\n" \ #"abc\r\n" #P2C: "+OK 1024 byte coming\r\n" \ #"abc\r\n" #S2P: Disconnect #. # Authentication test StartPolicy def config(self): Pop3Proxy.config(self) self.request["AUTH"] = POP3_REQ_ACCEPT EndPolicy #invalid AUTH #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "AUTH barmi\r\n" #P2C: "-ERR " $NIHIL "\r\n" #S2P: Disconnect #. #valid AUTH S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "AUTH barmi\r\n" P2S: "AUTH barmi\r\n" S2P: "+ AmFYig==\r\n" P2C: "+ AmFYig==\r\n" C2P: "BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT+nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oe\r\n" P2S: "BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT+nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oe\r\n" S2P: "+ or//EoAADZI=\r\n" P2C: "+ or//EoAADZI=\r\n" C2P: "DiAF5A4gA+oOIALuBkAAmw==\r\n" P2S: "DiAF5A4gA+oOIALuBkAAmw==\r\n" S2P: "+OK barmi authentication successful\r\n" P2C: "+OK barmi authentication successful\r\n" . #cancelled AUTH S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "AUTH barmi\r\n" P2S: "AUTH barmi\r\n" S2P: "+ AmFYig==\r\n" P2C: "+ AmFYig==\r\n" C2P: "*\r\n" P2S: "*\r\n" S2P: "-ERR authentication exchange failed\r\n" P2C: "-ERR authentication exchange failed\r\n" . # CAPA # #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "CAPA\r\n" #P2S: "CAPA\r\n" #S2P: "-ERR Invalid command, try one of: USER name, PASS string, APOP name digest, QUIT\r\n" #P2C: "-ERR Invalid command, try one of: USER name, PASS string, APOP name digest, QUIT\r\n" #C2P: "USER test\r\n" #P2S: "USER test\r\n" #S2P: "+OK test selected\r\n" #P2C: "+OK test selected\r\n" #C2P: "PASS test\r\n" #P2S: "PASS test\r\n" #S2P: "+OK maildrop locked and ready\r\n" #P2C: "+OK maildrop locked and ready\r\n" #. # #S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" #C2P: "CAPA\r\n" #P2S: "CAPA\r\n" #S2P: "+OK Capability list follows\r\n\ #TOP\r\n\ #USER\r\n\ #SASL CRAM-MD5\r\n\ #RESP-CODES\r\n\ #LOGIN-DELAY 900\r\n\ #PIPELINING\r\n\ #EXPIRE 30\r\n\ #UIDL\r\n\ #IMPLEMENTATION information\r\n.\r\n" #P2C: "+OK Capability list follows\r\n\ #TOP\r\n\ #USER\r\n\ #SASL CRAM-MD5\r\n\ #RESP-CODES\r\n\ #LOGIN-DELAY 900\r\n\ #PIPELINING\r\n\ #EXPIRE 30\r\n\ #UIDL\r\n\ #IMPLEMENTATION information\r\n.\r\n" #. #Bug 3893 S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "USER valaki\r\n" P2S: "USER valaki\r\n" S2P: "+OK name is a valid mailbox\r\n" P2C: "+OK name is a valid mailbox\r\n" C2P: "PASS barmi\r\n" P2S: "PASS barmi\r\n" S2P: "+OK maildrop locked and ready\r\n" P2C: "+OK maildrop locked and ready\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK 120 octets follows\r\n" \ "En egy message sor vagyok\r\n" \ "En pedig egy olyan sor" 0x00 0x00 0x00 " amiben null karakter van\r\n" \ "Ez megint egy normalis sor.\r\n" \ ".\r\n" P2C: "+OK 120 octets follows\r\n" \ "En egy message sor vagyok\r\n" \ "En pedig egy olyan sor" 0x00 0x00 0x00 " amiben null karakter van\r\n" \ "Ez megint egy normalis sor.\r\n" \ ".\r\n" . zorp-3.9.5/tests/functional/pop3/info000066400000000000000000000000261172670260400175700ustar00rootroot00000000000000Base-Class: Pop3Proxy zorp-3.9.5/tests/functional/pop3/transfer/000077500000000000000000000000001172670260400205405ustar00rootroot00000000000000zorp-3.9.5/tests/functional/pop3/transfer/pop3-virus-scan.tests000066400000000000000000000115331172670260400246000ustar00rootroot00000000000000StartGlobalInfo Tags virus bug10796 EndGlobalInfo StartPolicy def config(self): Pop3Proxy.config(self) self.response_stack["RETR"] = (POP3_STK_MIME, "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_REJECT\\\\nn[]Description\\\\nn Nem nyert\\\\nn\\\\nn'>&3; read alma <&3") self.reject_by_mail = FALSE EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK message follows\r\n" \ "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\r\n" \ ".\r\n" P2C: "-ERR Content rejected ( Nem nyert)\r\n" . StartPolicy def config(self): Pop3Proxy.config(self) self.response_stack["RETR"] = (POP3_STK_MIME, "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_REJECT\\\\nn[]Description\\\\nn Nem nyert\\\\nn\\\\nn'>&3; read alma <&3") self.reject_by_mail = TRUE EndPolicy StartInfo Tags qqq EndInfo S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK 684 octets\r\n\ From: Szalay Attila \r\n\ To: \r\n\ \r\n\ \r\n\ Subject: dfgdfgdgfdgdfgfdgdfgfdgdfgfdgdf dfg df dfg fgdf dfdf gdf gfdgfdd gdgfd gdfgdf gdf gdfgdfg dfgdfgdf gfdg dfgdfg fdg dfgdfg dfgdf gdf gdfg dfg dfgdfgfd\r\n\ dgfdfs dfgfd gdfgfd gfd gdfgfdg dfgfdg dfg dfgdf gfdg dfgd f gfdg dfg dfg df fdg dfg dfg dfgd fgdf gdff dgdfg df\r\n\ Content-Type: TEXT/PLAIN\r\n\ Content-Transfer-Encoding: 7bit\r\n\ \r\n\ X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\r\n\ .\r\n" P2C: "+OK " $NIHIL1 " octets\r\n\ From: Szalay Attila \r\n\ To: \r\n\ \r\n\ \r\n\ Subject: dfgdfgdgfdgdfgfdgdfgfdgdfgfdgdf dfg df dfg fgdf dfdf gdf gfdgfdd gdgfd gdfgdf gdf gdfgdfg dfgdfgdf gfdg dfgdfg fdg dfgdfg dfgdf gdf gdfg dfg dfgdfgfd\r\n\ dgfdfs dfgfd gdfgfd gfd gdfgfdg dfgfdg dfg dfgdf gfdg dfgd f gfdg dfg dfg df fdg dfg dfg dfgd fgdf gdff dgdfg df\r\n\ Content-Type: TEXT/PLAIN\r\n\ Content-Transfer-Encoding: 7bit\r\n\ \r\n\ " $NIHIL2 "\ \r\n\ Content rejected ( Nem nyert)\r\n\ .\r\n" . StartPolicy def config(self): Pop3Proxy.config(self) self.response_stack["RETR"] = (POP3_STK_MIME, "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_ERROR\\\\nn[]Description\\\\nn Nem nyert\\\\nn\\\\nn'>&3; read alma <&3") self.reject_by_mail = TRUE EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK message follows\r\n" \ "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\r\n" \ ".\r\n" P2C: "-ERR Error occurred while transferring data ( Nem nyert)\r\n" . StartPolicy def config(self): Pop3Proxy.config(self) self.response_stack["RETR"] = (POP3_STK_POLICY, self.doStack) def doStack(self, cmd): if cmd != 'RETR': raise ValueError, "Command should be RETR" return (POP3_STK_DATA, PlugProxy) EndPolicy S2P: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" P2C: "+OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>\r\n" C2P: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" P2S: "APOP mrose c4c9334bac560ecc979e58001b3e22fb\r\n" S2P: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" P2C: "+OK mrose's maildrop has 2 messages (320 octets)\r\n" C2P: "STAT\r\n" P2S: "STAT\r\n" S2P: "+OK 2 320\r\n" P2C: "+OK 2 320\r\n" C2P: "RETR 1\r\n" P2S: "RETR 1\r\n" S2P: "+OK message follows\r\n" \ "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\r\n" \ ".\r\n" P2C: "+OK message follows\r\n" \ "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\r\n" \ ".\r\n" . zorp-3.9.5/tests/functional/smtp/000077500000000000000000000000001172670260400170165ustar00rootroot00000000000000zorp-3.9.5/tests/functional/smtp/info000066400000000000000000000000261172670260400176720ustar00rootroot00000000000000Base-Class: SmtpProxy zorp-3.9.5/tests/functional/smtp/policy/000077500000000000000000000000001172670260400203155ustar00rootroot00000000000000zorp-3.9.5/tests/functional/smtp/policy/permit_long_responses.tests000066400000000000000000000005441172670260400260240ustar00rootroot00000000000000 StartGlobalInfo Tags bug6078 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.max_response_length = 10 self.permit_long_responses = TRUE EndPolicy S2P: "220 ESMTP\r\n" P2C: "220 ESMTP\r\n" C2P: "EHLO bb\r\n" P2S: "EHLO bb\r\n" S2P: "250 response longer than 10 characters\r\n" P2C: "250 respon\r\n" . zorp-3.9.5/tests/functional/smtp/policy/permit_omission_of_angle_brackets.tests000066400000000000000000000314541172670260400303400ustar00rootroot00000000000000StartGlobalInfo Tags bug17025 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains=("*",) self.relay_check=FALSE self.language="hu" self.permit_omission_of_angle_brackets=TRUE EndPolicy # Testcase 0 - RFC compliant operation S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: \r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 1 - No '<' '>' at all S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: sender@senderhost.org\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: receiver@receiverhost.org\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 2 - '<' '>' only in MAIL and ' ' at the end of RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: \r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: receiver@receiverhost.org \r\n" P2C: "500 Invalid command\r\n" C2P: "DATA\r\n" P2C: "503 Invalid command in this state\r\n" C2P: "asdf\r\n" P2C: "500 Invalid command\r\n" C2P: "asdfa\r\n" P2C: "500 Invalid command\r\n" C2P: "sdfas\r\n" P2C: "500 Invalid command\r\n" C2P: "neszeneszenesze\r\n" P2C: "500 Invalid command\r\n" C2P: ".\r\n" P2C: "500 Invalid command\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 2.1 - '<' '>' only in MAIL S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: \r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: receiver@receiverhost.org\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 3 - '<' '>' only in RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: sender@senderhost.org\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 3.1 - '<' '>' only in RCPT and SIZE at the end of MAIL S2P: "220 receiverhost.org ESMTP postfix\r\n" P2C: "220 receiverhost.org ESMTP postfix\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-receiverhost.org\r\n250-PIPELINING\r\n250-SIZE 10240000\r\n250-ETRN\r\n250 8BITMIME\r\n" P2C: "250-receiverhost.org\r\n250-PIPELINING\r\n250-SIZE 10240000\r\n250-ETRN\r\n250 8BITMIME\r\n" C2P: "MAIL From: sender@senderhost.org SIZE=10037\r\n" P2S: "MAIL From: SIZE=10037\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 3.2 - '<' '>' only in RCPT and ' ' at the end of MAIL S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: sender@senderhost.org \r\n" P2S: "MAIL From: \r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 4 - '<' in MAIL and '>' in RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: \r\n" P2C: "503 Invalid command in this state\r\n" C2P: "DATA\r\n" P2C: "503 Invalid command in this state\r\n" C2P: "asdf\r\n" P2C: "500 Invalid command\r\n" C2P: "asdfa\r\n" P2C: "500 Invalid command\r\n" C2P: "sdfas\r\n" P2C: "500 Invalid command\r\n" C2P: "neszeneszenesze\r\n" P2C: "500 Invalid command\r\n" C2P: ".\r\n" P2C: "500 Invalid command\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 5 - '>' in MAIL and '<' in RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: sender@senderhost.org>\r\n" P2C: "500 Invalid command\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" C2P: "RCPT To: \r\n" P2C: "503 Invalid command in this state\r\n" C2P: "DATA\r\n" P2C: "503 Invalid command in this state\r\n" C2P: "asdf\r\n" P2C: "500 Invalid command\r\n" C2P: "asdfa\r\n" P2C: "500 Invalid command\r\n" C2P: "sdfas\r\n" P2C: "500 Invalid command\r\n" C2P: "neszeneszenesze\r\n" P2C: "500 Invalid command\r\n" C2P: ".\r\n" P2C: "500 Invalid command\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 6 - '<,>' in the middle of the address in MAIL and RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: senerhost.org\r\n" P2C: "503 Invalid command in this state\r\n" C2P: "DATA\r\n" P2C: "503 Invalid command in this state\r\n" C2P: "asdf\r\n" P2C: "500 Invalid command\r\n" C2P: "asdfa\r\n" P2C: "500 Invalid command\r\n" C2P: "sdfas\r\n" P2C: "500 Invalid command\r\n" C2P: "neszeneszenesze\r\n" P2C: "500 Invalid command\r\n" C2P: ".\r\n" P2C: "500 Invalid command\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 7 - '<,>' in the middle of the address RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: \r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: receiver@receiver>host.org\r\n" P2C: "500 Invalid command\r\n" C2P: "DATA\r\n" P2C: "503 Invalid command in this state\r\n" C2P: "asdf\r\n" P2C: "500 Invalid command\r\n" C2P: "asdfa\r\n" P2C: "500 Invalid command\r\n" C2P: "sdfas\r\n" P2C: "500 Invalid command\r\n" C2P: "neszeneszenesze\r\n" P2C: "500 Invalid command\r\n" C2P: ".\r\n" P2C: "500 Invalid command\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 8 - '>' in the word RCPT S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From: \r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "R>CPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Testcase 9 - '>' in the word MAIL S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "M>AIL From: \r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n.\r\n" P2S: "asdf\r\nasdfa\r\nsdfas\r\nneszeneszenesze\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/policy/require_crlf.tests000066400000000000000000000011151172670260400240610ustar00rootroot00000000000000 StartGlobalInfo Tags bug2137 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.require_crlf = FALSE EndPolicy S2P: "220 ESMTP\n" P2C: "220 ESMTP\r\n" C2P: "EHLO bb\n" P2S: "EHLO bb\r\n" S2P: "250 ok\r\n" P2C: "250 ok\r\n" C2P: "QUIT\n" P2S: "QUIT\r\n" S2P: "221 Bye\r\n" P2C: "221 Bye\r\n" . StartPolicy def config(self): SmtpProxy.config(self) self.require_crlf = TRUE EndPolicy S2P: "220 ESMTP\r\n" P2C: "220 ESMTP\r\n" C2P: "EHLO bb\n" P2C: "421 Service not available, closing transmission channel.\r\n" . zorp-3.9.5/tests/functional/smtp/policy/response.tests000066400000000000000000000031111172670260400232330ustar00rootroot00000000000000StartGlobalInfo Tags bug6711 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org',) self.response["MAIL", "333"] = (SMTP_RSP_ACCEPT) EndPolicy #Check the answers for commands. S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<450@sender.host>\r\n" P2S: "MAIL From:<450@sender.host>\r\n" S2P: "450 Nem Ok.\r\n" P2C: "450 Nem Ok.\r\n" . #Check the answers for commands. S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<450@sender.host>\r\n" P2S: "MAIL From:<450@sender.host>\r\n" S2P: "250 Nem Ok.\r\n" P2C: "250 Nem Ok.\r\n" . #Check the answers for commands. S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<450@sender.host>\r\n" P2S: "MAIL From:<450@sender.host>\r\n" S2P: "333 Nem Ok.\r\n" P2C: "333 Nem Ok.\r\n" . StartInfo Tags bug7222 EndInfo #Check the answers for commands. S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "501 gond\r\n" P2C: "501 gond\r\n" . zorp-3.9.5/tests/functional/smtp/policy/softfail.tests000066400000000000000000000030051172670260400232060ustar00rootroot00000000000000StartGlobalInfo Tags bug7935 EndGlobalInfo StartPolicy class MyMatcher(AbstractMatcher): def checkMatch(self, str): return TRUE def config(self): SmtpProxy.config(self) self.error_soft = TRUE self.relay_check = FALSE self.sender_matcher = self.MyMatcher() EndPolicy #Check the answers for commands. S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<450@sender.host>\r\n" P2C: "450 Cannot verify sender at this time, come back later.\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "421 Bye\r\n" P2C: "421 Bye\r\n" . StartPolicy class MyMatcher(AbstractMatcher): def checkMatch(self, str): return TRUE def config(self): SmtpProxy.config(self) self.error_soft = TRUE self.relay_check = FALSE self.recipient_matcher = self.MyMatcher() EndPolicy #Check the answers for commands. S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<450@sender.host>\r\n" P2S: "MAIL From:<450@sender.host>\r\n" S2P: "250 Ok.\r\n" P2C: "250 Ok.\r\n" C2P: "RCPT To:<450@recipient.host>\r\n" P2C: "450 Cannot verify recipient at this time, come back later.\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "421 Bye\r\n" P2C: "421 Bye\r\n" . zorp-3.9.5/tests/functional/smtp/policy/timeout.tests000066400000000000000000000011421172670260400230650ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.require_crlf = FALSE self.timeout = 15000 EndPolicy S2P: "220 ESMTP\n" P2C: "220 ESMTP\r\n" C2P: "EHLO bb\n" P2S: "EHLO bb\r\n" S2P: "250" A=time.sleep(16) S2P: " ok\r\n" P2C: "421 Service not available, closing transmission channel.\r\n" P2C: Disconnect . S2P: "220 ESMTP\n" P2C: "220 ESMTP\r\n" C2P: "EHL" A=time.sleep(16) C2P: "O bb\n" P2C: "421 Service not available, closing transmission channel.\r\n" P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/protocol/000077500000000000000000000000001172670260400206575ustar00rootroot00000000000000zorp-3.9.5/tests/functional/smtp/protocol/accounting_log.tests000066400000000000000000000075441172670260400247500ustar00rootroot00000000000000StartGlobalInfo Tags bug8586 EndGlobalInfo # In fact, these cases don't test anything but produce situations where # some certain 'Accounting' log messages are produced. StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org') EndPolicy S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" P2C: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" C2P: "MAIL From: SIZE=10000\r\n" P2S: "MAIL From: SIZE=10000\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "250 Borsodi\r\n" P2C: "250 Borsodi\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" P2C: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" C2P: "MAIL From: SIZE=10000\r\n" P2S: "MAIL From: SIZE=10000\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "451 Soproni\r\n" P2C: "451 Soproni\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" P2C: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" C2P: "MAIL From: SIZE=10000\r\n" P2S: "MAIL From: SIZE=10000\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "503 Staropramen\r\n" P2C: "503 Staropramen\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" P2C: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" C2P: "MAIL From: SIZE=10000\r\n" P2S: "MAIL From: SIZE=10000\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "999 Csapviz\r\n" P2C: "421 Service not available, closing transmission channel.\r\n" . zorp-3.9.5/tests/functional/smtp/protocol/basic.tests000066400000000000000000000605021172670260400230270ustar00rootroot00000000000000 StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org',) EndPolicy # Some valid scenarios/MAIL command S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "552 Recipient storage full, try again in another transaction\r\n" P2C: "552 Recipient storage full, try again in another transaction\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" #
C2P: "Subject: test\r\nFrom: Sender User \r\nDate: 15-Sept-03 12:15:44\r\nTo: Receiver User \r\n\r\n\r\n.\r\n" P2S: "Subject: test\r\nFrom: Sender User \r\nDate: 15-Sept-03 12:15:44\r\nTo: Receiver User \r\n\r\n\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Forwarding with positive preliminary and permanent negative completion reply S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "251 User not local, will forwarding to \r\n" P2C: "251 User not local, will forwarding to \r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "551 User not local, please try \r\n" P2C: "551 User not local, please try \r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Mailbox not found S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "550 No such user here\r\n" P2C: "550 No such user here\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # SEND, SOML, SAML commands # #S2P: "220 receiverhost.org SMTP ready\r\n" #P2C: "220 receiverhost.org SMTP ready\r\n" #C2P: "HELO senderhost.org\r\n" #P2S: "HELO senderhost.org\r\n" #S2P: "250 receiverhost.org\r\n" #P2C: "250 receiverhost.org\r\n" #C2P: "SEND From:\r\n" #P2S: "SEND From:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "RCPT To:\r\n" #P2S: "RCPT To:\r\n" #S2P: "450 User not active now\r\n" #P2C: "450 User not active now\r\n" #C2P: "RCPT To:\r\n" #P2S: "RCPT To:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "DATA\r\n" #P2S: "DATA\r\n" #S2P: "354 Start mail input\r\n" #P2C: "354 Start mail input\r\n" #C2P: "
\r\n\r\n\r\n.\r\n" #P2S: "
\r\n\r\n\r\n\r\n.\r\n" #S2P: "250 Ok\r\n" #P2C: "250 Ok\r\n" #C2P: "QUIT\r\n" #P2S: "QUIT\r\n" #S2P: "221 receiverhost.org Service closing transmission channel\r\n" #P2C: "221 receiverhost.org Service closing transmission channel\r\n" #C2P: Disconnect #P2S: Disconnect #. # #S2P: "220 receiverhost.org SMTP ready\r\n" #P2C: "220 receiverhost.org SMTP ready\r\n" #C2P: "HELO senderhost.org\r\n" #P2S: "HELO senderhost.org\r\n" #S2P: "250 receiverhost.org\r\n" #P2C: "250 receiverhost.org\r\n" #C2P: "SOML From:\r\n" #P2S: "SOML From:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "RCPT To:\r\n" #P2S: "RCPT To:\r\n" #S2P: "250 User not active now, so will do mail\r\n" #P2C: "250 User not active now, so will do mail\r\n" #C2P: "DATA\r\n" #P2S: "DATA\r\n" #S2P: "354 Start mail input\r\n" #P2C: "354 Start mail input\r\n" #C2P: "
\r\n\r\n\r\n.\r\n" #P2S: "
\r\n\r\n\r\n\r\n.\r\n" #S2P: "250 Ok\r\n" #P2C: "250 Ok\r\n" #C2P: "QUIT\r\n" #P2S: "QUIT\r\n" #S2P: "221 receiverhost.org Service closing transmission channel\r\n" #P2C: "221 receiverhost.org Service closing transmission channel\r\n" #C2P: Disconnect #P2S: Disconnect #. # #S2P: "220 receiverhost.org SMTP ready\r\n" #P2C: "220 receiverhost.org SMTP ready\r\n" #C2P: "HELO senderhost.org\r\n" #P2S: "HELO senderhost.org\r\n" #S2P: "250 receiverhost.org\r\n" #P2C: "250 receiverhost.org\r\n" #C2P: "SAML From:\r\n" #P2S: "SAML From:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "RCPT To:\r\n" #P2S: "RCPT To:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "DATA\r\n" #P2S: "DATA\r\n" #S2P: "354 Start mail input\r\n" #P2C: "354 Start mail input\r\n" #C2P: "
\r\n\r\n\r\n.\r\n" #P2S: "
\r\n\r\n\r\n\r\n.\r\n" #S2P: "250 Ok\r\n" #P2C: "250 Ok\r\n" #C2P: "QUIT\r\n" #P2S: "QUIT\r\n" #S2P: "221 receiverhost.org Service closing transmission channel\r\n" #P2C: "221 receiverhost.org Service closing transmission channel\r\n" #C2P: Disconnect #P2S: Disconnect #. # RSET command / Aborted scenario S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "550 No such user here\r\n" P2C: "550 No such user here\r\n" C2P: "RSET\r\n" P2S: "RSET\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . StartPolicy def config(self): SmtpProxy.config(self) self.request["VRFY"] = (SMTP_REQ_ACCEPT,) self.response["VRFY", "250"] = SMTP_REQ_ACCEPT self.response["VRFY", "251"] = SMTP_REQ_ACCEPT self.response["VRFY", "421"] = SMTP_REQ_ACCEPT self.response["VRFY", "500"] = SMTP_REQ_ACCEPT self.response["VRFY", "501"] = SMTP_REQ_ACCEPT self.response["VRFY", "502"] = SMTP_REQ_ACCEPT self.response["VRFY", "504"] = SMTP_REQ_ACCEPT self.response["VRFY", "550"] = SMTP_REQ_ACCEPT self.response["VRFY", "551"] = SMTP_REQ_ACCEPT self.response["VRFY", "553"] = SMTP_REQ_ACCEPT self.relay_domains = ('receiverhost.org') EndPolicy # VRFY and EXPN commands S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "VRFY User\r\n" P2S: "VRFY User\r\n" S2P: "250 Receiver User \r\n" P2C: "250 Receiver User \r\n" C2P: "VRFY Tester\r\n" P2S: "VRFY Tester\r\n" S2P: "250 \r\n" P2C: "250 \r\n" C2P: "VRFY Smith\r\n" P2S: "VRFY Smith\r\n" S2P: "251 User not local, will forward to \r\n" P2C: "251 User not local, will forward to \r\n" C2P: "VRFY Jones\r\n" P2S: "VRFY Jones\r\n" S2P: "551 User not local, please try \r\n" P2C: "551 User not local, please try \r\n" C2P: "VRFY Hommer\r\n" P2S: "VRFY Hommer\r\n" S2P: "550 String does not mach anything\r\n" P2C: "550 String does not mach anything\r\n" C2P: "VRFY Xzyqswer\r\n" P2S: "VRFY Xzyqswer\r\n" S2P: "553 User ambiguous\r\n" P2C: "553 User ambiguous\r\n" C2P: "VRFY List-Name\r\n" P2S: "VRFY List-Name\r\n" S2P: "550 This is not an username\r\n" P2C: "550 This is not an username\r\n" . StartPolicy def config(self): SmtpProxy.config(self) self.request["EXPN"] = (SMTP_REQ_ACCEPT,) self.response["EXPN", "250"] = SMTP_REQ_ACCEPT self.response["EXPN", "421"] = SMTP_REQ_ACCEPT self.response["EXPN", "500"] = SMTP_REQ_ACCEPT self.response["EXPN", "501"] = SMTP_REQ_ACCEPT self.response["EXPN", "502"] = SMTP_REQ_ACCEPT self.response["EXPN", "504"] = SMTP_REQ_ACCEPT self.response["EXPN", "550"] = SMTP_REQ_ACCEPT self.relay_domains = ('receiverhost.org') EndPolicy S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "EXPN Linux-List\r\n" P2S: "EXPN Linux-List\r\n" S2P: "250-Receiver User \r\n\ 250-\r\n\ 250 \r\n" P2C: "250-Receiver User \r\n\ 250-\r\n\ 250 \r\n" C2P: "EXPN Windows-List\r\n" P2S: "EXPN Windows-List\r\n" S2P: "550 Access Denied to You\r\n" P2C: "550 Access Denied to You\r\n" C2P: "EXPN Bill Smith\r\n" P2S: "EXPN Bill Smith\r\n" S2P: "550 This is not a mailing list\r\n" P2C: "550 This is not a mailing list\r\n" . StartPolicy def config(self): SmtpProxy.config(self) self.request["HELP"] = (SMTP_REQ_ACCEPT,) self.response["HELP", "211"] = SMTP_REQ_ACCEPT self.response["HELP", "214"] = SMTP_REQ_ACCEPT self.response["HELP", "502"] = SMTP_REQ_ACCEPT self.response["HELP", "504"] = SMTP_REQ_ACCEPT self.request["NOOP"] = (SMTP_REQ_ACCEPT,) self.response["NOOP", "250"] = SMTP_REQ_ACCEPT self.relay_domains = ('receiverhost.org') EndPolicy # HELP and NOOP commands S2P: "220 receiverhost SMTP ready\r\n" P2C: "220 receiverhost SMTP ready\r\n" C2P: "HELO senderhost\r\n" P2S: "HELO senderhost\r\n" S2P: "250 receiverhost\r\n" P2C: "250 receiverhost\r\n" C2P: "HELP\r\n" P2S: "HELP\r\n" S2P: "214-Commands:\r\n\ 214-HELO MAIL RCPT DATA RSET\r\n\ 214-NOOP QUIT HELP VRFY EXPN\r\n\ 214-For more info use HELP .\r\n\ 214-For local information contact postmaster at this site.\r\n\ 214 End of HELP info\r\n" P2C: "214-Commands:\r\n\ 214-HELO MAIL RCPT DATA RSET\r\n\ 214-NOOP QUIT HELP VRFY EXPN\r\n\ 214-For more info use HELP .\r\n\ 214-For local information contact postmaster at this site.\r\n\ 214 End of HELP info\r\n" C2P: "HELP [ RSET]\r\n" P2S: "HELP [ RSET]\r\n" S2P: "214-RSET\r\n\ 214-Resets the system.\r\n\ 214 End of HELP info\r\n" P2C: "214-RSET\r\n\ 214-Resets the system.\r\n\ 214 End of HELP info\r\n" C2P: "NOOP\r\n" P2S: "NOOP\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" . #StartPolicy # def config(self): # SmtpProxy.config(self) # self.relay_domains = ('host2.org','host1.org',) #EndPolicy # # TURN command/Change roles # #S2P: "220 host2.org SMTP ready\r\n" #P2C: "220 host2.org SMTP ready\r\n" #C2P: "HELO host1.org\r\n" #P2S: "HELO host1.org\r\n" #S2P: "250 host2.org\r\n" #P2C: "250 host2.org\r\n" #C2P: "MAIL From:\r\n" #P2S: "MAIL From:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "RCPT To:\r\n" #P2S: "RCPT To:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "DATA\r\n" #P2S: "DATA\r\n" #S2P: "354 Start mail input\r\n" #P2C: "354 Start mail input\r\n" #C2P: "
\r\n\r\n\r\n.\r\n" #P2S: "
\r\n\r\n\r\n\r\n.\r\n" #S2P: "250 Ok\r\n" #P2C: "250 Ok\r\n" #C2P: "TURN\r\n" #P2S: "TURN\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #S2P: "220 host1.org SMTP ready\r\n" #P2C: "220 host1.org SMTP ready\r\n" #C2P: "HELO host2.org\r\n" #P2S: "HELO host2.org\r\n" #S2P: "250 host1.org\r\n" #P2C: "250 host1.org\r\n" #C2P: "QUIT\r\n" #P2S: "QUIT\r\n" #S2P: "221 host1.org Service closing transmission channel\r\n" #P2C: "221 host1.org Service closing transmission channel\r\n" #C2P: Disconnect #P2S: Disconnect #. # #S2P: "220 host1.org SMTP ready\r\n" #P2C: "220 host1.org SMTP ready\r\n" #C2P: "HELO host2.org\r\n" #P2S: "HELO host2.org\r\n" #S2P: "250 host1.org\r\n" #P2C: "250 host1.org\r\n" #C2P: "TURN\r\n" #P2S: "TURN\r\n" #S2P: "502 Refused transmission\r\n" #P2C: "502 Refused transmission\r\n" #C2P: "QUIT\r\n" #P2S: "QUIT\r\n" #S2P: "221 host1.org Service closing transmission channel\r\n" #P2C: "221 host1.org Service closing transmission channel\r\n" #C2P: Disconnect #P2S: Disconnect #. StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org') EndPolicy # Extended HELO testing # bug5148 S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" P2C: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" C2P: "MAIL From: SIZE=10000\r\n" P2S: "MAIL From: SIZE=10000\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" . # ETRN command S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250 ETRN\r\n" P2C: "250-server\r\n\ 250 ETRN\r\n" C2P: "ETRN receiverhost.org\r\n" P2S: "ETRN receiverhost.org\r\n" S2P: "250 OK, queuing for node receiverhost.org started\r\n" P2C: "250 OK, queuing for node receiverhost.org started\r\n" C2P: "ETRN receiverhost.org\r\n" P2S: "ETRN receiverhost.org\r\n" S2P: "251 OK, no messages waiting for node receiverhost.org started\r\n" P2C: "251 OK, no messages waiting for node receiverhost.org started\r\n" C2P: "ETRN receiverhost.org\r\n" P2S: "ETRN receiverhost.org\r\n" S2P: "252 OK, pending messages for node receiverhost.org started\r\n" P2C: "252 OK, pending messages for node receiverhost.org started\r\n" C2P: "ETRN mysoft.com\r\n" P2S: "ETRN mysoft.com\r\n" S2P: "253 OK, 14 pending messages for node mysoft.com started\r\n" P2C: "253 OK, 14 pending messages for node mysoft.com started\r\n" C2P: "ETRN uu.net\r\n" P2S: "ETRN uu.net\r\n" S2P: "458 Unable to queue messages for node uu.net\r\n" P2C: "458 Unable to queue messages for node uu.net\r\n" C2P: "ETRN foo.bar\r\n" P2S: "ETRN foo.bar\r\n" S2P: "459 Node foo.bar not allowed: Unable to resolve name\r\n" P2C: "459 Node foo.bar not allowed: Unable to resolve name\r\n" C2P: "ETRN\r\n" P2C: "500" $NIHIL "\r\n" . # Valid command syntax S2P: "220 receiverhost SMTP ready\r\n" P2C: "220 receiverhost SMTP ready\r\n" C2P: "HELo senderhost\r\n" P2S: "HELO senderhost\r\n" S2P: "250 receiverhost\r\n" P2C: "250 receiverhost\r\n" C2P: "MaIl FrOm:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RcPT To: \r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DaTA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" . # Invalid command syntax # D-01211 # bug20230 S2P: "220 receiverhost SMTP ready\r\n" P2C: "220 receiverhost SMTP ready\r\n" C2P: "HELO senderhost\r\n" P2C: "500" $NIHIL "\r\n" C2P: "HELO \r\n" #P2S: "MAIL From:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "HELP\r\n" #P2C: "500" $NIHIL "\r\n" #C2P: "RCPT To:\r\n" #P2S: "RCPT To:\r\n" #S2P: "250 OK\r\n" #P2C: "250 OK\r\n" #C2P: "HELO senderhost.org\r\n" #P2C: "503" $NIHIL "\r\n" #C2P: "DATA\r\n" #P2S: "DATA\r\n" #S2P: "354 Start mail input\r\n" #P2C: "354 Start mail input\r\n" #C2P: "
\r\n\r\n\r\n.\r\n" #P2S: "
\r\n\r\n\r\n\r\n.\r\n" #S2P: "250 Ok\r\n" #P2C: "250 Ok\r\n" #C2P: "RCPT To:\r\n" #P2C: "503" $NIHIL "\r\n" #. StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('host4','host3','host2','host1',) EndPolicy # Relaying via more hosts S2P: "220 host2 SMTP ready\r\n" P2C: "220 host2 SMTP ready\r\n" C2P: "HELO host1\r\n" P2S: "HELO host1\r\n" S2P: "250 host2\r\n" P2C: "250 host2\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:<@host2,@host3:sam@host4>\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "Subject: test\r\nFrom: Joe Joe \r\nDate: 15-Sept-03 12:15:44\r\nTo: Sam Sam \r\n\r\n\r\n.\r\n" P2S: "Subject: test\r\nFrom: Joe Joe \r\nDate: 15-Sept-03 12:15:44\r\nTo: Sam Sam \r\n\r\n\r\n\r\n.\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" . # Message with timestamp S2P: "220 host3 SMTP ready\r\n" P2C: "220 host3 SMTP ready\r\n" C2P: "HELO host2\r\n" P2S: "HELO host2\r\n" S2P: "250 host3\r\n" P2C: "250 host3\r\n" C2P: "MAIL From:<@host2:joe@host1>\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:<@host3:sam@host4>\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" # C2P: "Return-Path: <@host2,@host1:joe@host1>\r\nReceived: from host2 by host3 ; 15 Sept 03 12:27:36 PST\r\nReceived: from host1 by host2 ; 15 Sept 03 12:16:07 PST\r\nDate: 15-Sept-03 12:15:44\r\nFrom: Joe Joe \r\nSubject: test\r\nTo: Sam Sam \r\n\r\n\r\n.\r\n" P2S: "Return-Path: <@host2,@host1:joe@host1>\r\nReceived: from host2 by host3 ; 15 Sept 03 12:27:36 PST\r\nReceived: from host1 by host2 ; 15 Sept 03 12:16:07 PST\r\nDate: 15-Sept-03 12:15:44\r\nFrom: Joe Joe \r\nSubject: test\r\nTo: Sam Sam \r\n\r\n\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" . # Undeliverable mail notification message S2P: "220 host2 SMTP ready\r\n" P2C: "220 host2 SMTP ready\r\n" C2P: "HELO host3\r\n" P2S: "HELO host3\r\n" S2P: "250 host2\r\n" P2C: "250 host2\r\n" C2P: "MAIL From:<>\r\n" P2S: "MAIL From:<>\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:<@host2:joe@host1>\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "Subject: Mail System Problem\r\nSender: SMTP@host4\r\nDate: 15-Sept-03 12:15:44\r\n\r\nSorry Joe, your message to sam@host4 lost.\r\nhost4 said this: 550 No Such User\r\n.\r\n" P2S: "Subject: Mail System Problem\r\nSender: SMTP@host4\r\nDate: 15-Sept-03 12:15:44\r\n\r\nSorry Joe, your message to sam@host4 lost.\r\nhost4 said this: 550 No Such User\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 host2 Service closing transmission channel\r\n" P2C: "221 host2 Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . # Invalid address parses S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<@:sender.senderhost.org>\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:<:receiver@receiverhost.org>\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:receiver@receiverhost.org\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:<@host1receiver@receiverhost.org>\r\n" P2C: "500" $NIHIL "\r\n" C2P: "MAIL From:<@host1,receiver@receiverhost.org>\r\n" P2C: "500" $NIHIL "\r\n" . # Valid address parses QUOTE='"' S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:<" %QUOTE "sender user" %QUOTE "@senderhost.org>\r\n" P2S: "MAIL From:<" %QUOTE "sender user" %QUOTE "@senderhost.org>\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" . # checks whether '%' and '!' are permitted (should not be) # tests bug #3154 S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2C: "501 Malformed address\r\n" C2P: "RCPT To:\r\n" P2C: "501 Malformed address\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL FROM:" 00 "\r\n" P2S: "MAIL From:\r\n" S2P: "250 ok\r\n" P2C: "250 ok\r\n" . #S2P: "554 receiverhost.org SMTP ready\r\n" #P2C: "554 receiverhost.org SMTP ready\r\n" #C2P: Disconnect #P2C: "421 Service not available, closing transmission channel.\r\n" #P2C: Disconnect#. zorp-3.9.5/tests/functional/smtp/protocol/dotdot.tests000066400000000000000000000031601172670260400232400ustar00rootroot00000000000000StartGlobalInfo Tags bug13339 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org',) EndPolicy # Some valid scenarios/MAIL command S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "552 Recipient storage full, try again in another transaction\r\n" P2C: "552 Recipient storage full, try again in another transaction\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" #
C2P: "Subject: test\r\nFrom: Sender User \r\nDate: 15-Sept-03 12:15:44\r\nTo: Receiver User \r\n\r\n..\r\n..\r\n..\r\n..\r\n.\r\n" P2S: "Subject: test\r\nFrom: Sender User \r\nDate: 15-Sept-03 12:15:44\r\nTo: Receiver User \r\n\r\n..\r\n..\r\n..\r\n..\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/protocol/extensions-parsing.tests000066400000000000000000000040301172670260400256000ustar00rootroot00000000000000 StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org') EndPolicy StartInfo Tags bug5148 EndInfo # Extended HELO testing # bug5148 S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" P2C: "250-server\r\n\ 250-PIPELINING\r\n\ 250-SIZE 10240000\r\n\ 250-ETRN\r\n\ 250 8BITMIME\r\n" C2P: "MAIL From: SIZE=10000\r\n" P2S: "MAIL From: SIZE=10000\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" . StartInfo Tags bug6111 EndInfo # case insensitivity of mail extensions S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "EHLO senderhost.org\r\n" P2S: "EHLO senderhost.org\r\n" S2P: "250-server\r\n\ 250-PiPeLiNiNG\r\n\ 250-SIzE 10240000\r\n\ 250-EtRN\r\n\ 250 8bitMIME\r\n" P2C: "250-server\r\n\ 250-PiPeLiNiNG\r\n\ 250-SIzE 10240000\r\n\ 250-EtRN\r\n\ 250 8bitMIME\r\n" C2P: "MAIL From: sIzE=10000 bOdY=7BIT\r\n" P2S: "MAIL From: SIZE=10000 BODY=7BIT\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" C2P: "
\r\n\r\n\r\n.\r\n" P2S: "
\r\n\r\n\r\n\r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" . zorp-3.9.5/tests/functional/smtp/protocol/long-lines.tests000066400000000000000000000037021172670260400240140ustar00rootroot00000000000000StartGlobalInfo Tags bug9299 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org',) self.max_line_length = 64 EndPolicy # Some valid scenarios/MAIL command S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "552 Recipient storage full, try again in another transaction\r\n" P2C: "552 Recipient storage full, try again in another transaction\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" #
C2P: "Subject: test\r\n\ From: Sender User \r\n\ Date: 15-Sept-03 12:15:44\r\n\ To: Receiver User \r\n\ \r\n\ \r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ .\r\n" P2S: "Subject: test\r\n\ From: Sender User \r\n\ Date: 15-Sept-03 12:15:44\r\n\ To: Receiver User \r\n\ \r\n\ \r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ " "1234567890"x30 "\r\n\ \r\n\ .\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/protocol/server-unreachable.tests000066400000000000000000000005441172670260400255230ustar00rootroot00000000000000 StartGlobalInfo Tags bug5723 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.unconnected_response_code = 421 def connectServer(self): return None EndPolicy P2C: "421 Server not available\r\n" C2P: "EHLO bb\r\n" P2C: "503 Server not available\r\n" C2P: "QUIT\r\n" P2C: "221 Bye\r\n" . zorp-3.9.5/tests/functional/smtp/transfer/000077500000000000000000000000001172670260400206425ustar00rootroot00000000000000zorp-3.9.5/tests/functional/smtp/transfer/bug20030.tests000066400000000000000000000077061172670260400231020ustar00rootroot00000000000000StartGlobalInfo Tags bug20030 EndGlobalInfo StartPolicy pass class MyMimeProxy(MimeProxy): def config(self): MimeProxy.config(self) self.body_type["image", "jpeg"]=(MIME_TPE_DROP_ONE,) self.silent_drop=FALSE def config(self): SmtpProxy.config(self) self.request_stack["*"]=(SMTP_STK_MIME, self.MyMimeProxy) #self.request_stack["*"]=(SMTP_STK_MIME, (Z_STACK_PROXY, self.MyMimeProxy)) self.relay_check=FALSE self.relay_domains = ("receiverhost.org",) EndPolicy # Some valid scenarios/MAIL command QUOTE=chr(34) S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "NOOP\r\n" S2P: "250 2.0.0 Ok\r\n" P2C: "354 Go on, send your message\r\n" #
C2P: "Subject: Multipart Test\r\n\ From: Sender User \r\n\ Date: Wed, 7 Oct 2009 14:37:01\r\n\ To: Receiver User \r\n\ MIME-Version: 1.0\r\n\ Content-Type: multipart/mixed; boundary=" %QUOTE "EVF5PPMfhYS0aIcm" %QUOTE "\r\n\ Content-Disposition: inline\r\n\ Content-Length: 950\r\n\ \r\n\ --EVF5PPMfhYS0aIcm\r\n\ Content-Type: text/plain; charset=us-ascii\r\n\ Content-Disposition: inline\r\n\ \r\n\ Multipart Test\r\n\ \r\n\ --EVF5PPMfhYS0aIcm\r\n\ Content-Type: image/jpeg\r\n\ Content-Disposition: attachment; filename=" %QUOTE "zts.jpeg" %QUOTE "\r\n\ Content-Transfer-Encoding: base64\r\n\ \r\n\ /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\r\n\ AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEB\r\n\ AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAAR\r\n\ CAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAUEAEAAAAAAAAAAAAA\r\n\ AAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAACP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAM\r\n\ AwEAAhEDEQA/AJ3AH84H/9k=\r\n\ \r\n\ --EVF5PPMfhYS0aIcm\r\n\ Content-Type: image/png\r\n\ Content-Disposition: attachment; filename=" %QUOTE "zts.png" %QUOTE "\r\n\ Content-Transfer-Encoding: base64\r\n\ \r\n\ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAlwSFlz\r\n\ AAALEwAACxMBAJqcGAAAAAd0SU1FB9kKBwwjOl89kOgAAAAMSURBVAjXY5iWGAwAAtsBSziV\r\n\ JjsAAAAASUVORK5CYII=\r\n\ \r\n\ --EVF5PPMfhYS0aIcm--\r\n\ .\r\n" P2S: "DATA\r\n" S2P: "354 End data with .\r\n" P2S: "Subject: Multipart Test\r\n\ From: Sender User \r\n\ Date: Wed, 7 Oct 2009 14:37:01\r\n\ To: Receiver User \r\n\ MIME-Version: 1.0\r\n\ Content-Type: multipart/mixed; boundary=" %QUOTE "EVF5PPMfhYS0aIcm" %QUOTE "\r\n\ Content-Disposition: inline\r\n\ Content-Length: 950\r\n\ \r\n\ \r\n\ --EVF5PPMfhYS0aIcm\r\n\ Content-Type: text/plain; charset=us-ascii\r\n\ Content-Disposition: inline\r\n\ \r\n\ Multipart Test\r\n\ \r\n\ --EVF5PPMfhYS0aIcm\r\n\ Content-Type: TEXT/PLAIN\r\n\ Content-Transfer-Encoding: 7bit\r\n\ \r\n\ The original content of this attachment was rejected by local policy\r\n\ settings.\r\n\ \r\n\ Message denied by policy\r\n\ \r\n\ --EVF5PPMfhYS0aIcm\r\n\ Content-Type: image/png\r\n\ Content-Disposition: attachment; filename=" %QUOTE "zts.png" %QUOTE "\r\n\ Content-Transfer-Encoding: base64\r\n\ \r\n\ iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAlwSFlz\r\n\ AAALEwAACxMBAJqcGAAAAAd0SU1FB9kKBwwjOl89kOgAAAAMSURBVAjXY5iWGAwAAtsBSziV\r\n\ JjsAAAAASUVORK5CYII=\r\n\ \r\n\ --EVF5PPMfhYS0aIcm--\r\n\ \r\n\ .\r\n" S2P: "250 Ok: queued as \r\n" P2C: "250 Ok: queued as \r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/transfer/message-drop.tests000066400000000000000000000027031172670260400243160ustar00rootroot00000000000000 StartGlobalInfo Tags bug9459 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) drop_all_prg = "/bin/cat >/dev/null; /bin/echo -en '0 SETVERDICT\\\\nn[]Verdict\\\\nnZ_DROP\\\\nn[]Description\\\\nnMert csak\\\\nn\\\\nn'>&3; read alma <&3" self.request_stack["DATA"] = (SMTP_STK_MIME, drop_all_prg) self.relay_domains = ('receiverhost.org',) EndPolicy S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "NOOP\r\n" S2P: "250 noop okay\r\n" P2C: "354 Go on, send your message\r\n" C2P: "Subject: test\r\n\ From: Sender User \r\n\ Date: 15-Sept-03 12:15:44\r\n\ To: Receiver User \r\n\ \r\n\ \r\n\ .\r\n" P2S: "RSET\r\n" S2P: "250 rset OK\r\n" P2C: "250 Message discarded (Mert csak)\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/transfer/received-header.tests000066400000000000000000000030301172670260400247360ustar00rootroot00000000000000StartGlobalInfo Tags bug10455 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org',) self.add_received_header = TRUE EndPolicy # Some valid scenarios/MAIL command S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "354 Start mail input\r\n" P2C: "354 Start mail input\r\n" #
C2P: "Subject: test\r\n\ From: Sender User \r\n\ Date: 15-Sept-03 12:15:44\r\n\ To: Receiver User \r\n\ \r\n\ \r\n\ .\r\n" P2S: "Received: from senderhost.org ([127.0.0.1]) by unknown with SMTP id svc/test1.0; " $DATE "\r\n\ Subject: test\r\n\ From: Sender User \r\n\ Date: 15-Sept-03 12:15:44\r\n\ To: Receiver User \r\n\ \r\n\ \r\n\ \r\n.\r\n" S2P: "250 Ok\r\n" P2C: "250 Ok\r\n" C2P: "QUIT\r\n" P2S: "QUIT\r\n" S2P: "221 receiverhost.org Service closing transmission channel\r\n" P2C: "221 receiverhost.org Service closing transmission channel\r\n" C2P: Disconnect P2S: Disconnect . zorp-3.9.5/tests/functional/smtp/transfer/unexpected-server-drop.tests000066400000000000000000000030321172670260400263360ustar00rootroot00000000000000 StartGlobalInfo Tags bug12918 EndGlobalInfo StartPolicy def config(self): SmtpProxy.config(self) self.relay_domains = ('receiverhost.org',) EndPolicy S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "592 Almafa az en viragom\r\n" S2P: Disconnect P2C: "500 Invalid command\r\n" C2P: "QUIT\r\n" P2C: "421 Service not available, closing transmission channel.\r\n" . S2P: "220 receiverhost.org SMTP ready\r\n" P2C: "220 receiverhost.org SMTP ready\r\n" C2P: "HELO senderhost.org\r\n" P2S: "HELO senderhost.org\r\n" S2P: "250 receiverhost.org\r\n" P2C: "250 receiverhost.org\r\n" C2P: "MAIL From:\r\n" P2S: "MAIL From:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "RCPT To:\r\n" P2S: "RCPT To:\r\n" S2P: "250 OK\r\n" P2C: "250 OK\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: "592 Almafa az en viragom\r\n" P2C: "500 Invalid command\r\n" C2P: "DATA\r\n" P2S: "DATA\r\n" S2P: Disconnect P2C: "421 Service not available, closing transmission channel.\r\n" . zorp-3.9.5/tests/functional/telnet/000077500000000000000000000000001172670260400173265ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/attr/000077500000000000000000000000001172670260400203005ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/attr/cases/000077500000000000000000000000001172670260400213765ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/attr/cases/testcases000066400000000000000000000126411172670260400233230ustar00rootroot00000000000000 #### StartPolicy ############################################################### StartPolicy def config(self): TelnetProxyStrict.config(self) self.option[TELNET_ENVIRONMENT, TELNET_SB_IS] = (TELNET_OPT_POLICY, self.rewriteVar) self.timeout = 120000 def rewriteVar(self, option, name, value): if self.current_var_name == "DISPLAY": self.current_var_value = "rewritten:0" return TELNET_OPT_ACCEPT EndPolicy IAC=255 SE=240 SB=250 WILL=251 WONT=252 DO=253 DONT=254 EOR=239 SEND=1 IS=0 INFO=2 VAR=0 VALUE=1 ESC=2 USERVAR=3 # policy rewrite C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: IAC SB 39 SEND VAR "DISPLAY" IAC SE P2S: IAC SB 39 SEND VAR "DISPLAY" IAC SE S2P: IAC SB 39 IS VAR "DISPLAY" VALUE "durin:0" IAC SE P2C: IAC SB 39 IS VAR "DISPLAY" VALUE "rewritten:0" IAC SE . # big data chunk C2P: IAC DO 25 P2S: IAC DO 25 S2P: IAC WILL 25 P2C: IAC WILL 25 C2P: IAC WILL 25 P2S: IAC WILL 25 S2P: IAC DO 25 P2C: IAC DO 25 C2P: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IAC EOR P2S: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IAC EOR . # policy rewrite - on block boundary C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IAC SB 39 SEND VAR "DISPLAY" IAC SE P2S: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IAC SB 39 SEND VAR "DISPLAY" IAC SE S2P: "lecso" P2C: "lecso" . StartPolicy def config(self): TelnetProxyStrict.config(self) self.timeout = 2000 EndPolicy # policy rewrite C2P: IAC DO 39 P2S: IAC DO 39 A= time.sleep(3) S2P: IAC WILL 39 ###. zorp-3.9.5/tests/functional/telnet/attr/cases/timeout.tests000066400000000000000000000010631172670260400241500ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): self.option["*"] = TELNET_OPT_ACCEPT self.negotiation["1"] = 2 self.negotiation["3"] = TELNET_NEG_NONE self.timeout = 3000 EndPolicy IAC=255 SE=240 SB=250 WILL=251 WONT=252 DO=253 DONT=254 SEND=1 IS=0 INFO=2 VAR=0 VALUE=1 ESC=2 USERVAR=3 C2P: IAC A=time.sleep(6) C2P: 3 "Negy" P2S: Disconnect . C2P: IAC 3 "Negy" P2S: IAC 3 "Negy" S2P: IAC A=time.sleep(6) S2P: 3 "Ot" P2C: Disconnect . zorp-3.9.5/tests/functional/telnet/attr/config000066400000000000000000000000001172670260400214560ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/attr/info000066400000000000000000000000131172670260400211500ustar00rootroot00000000000000Tags: attr zorp-3.9.5/tests/functional/telnet/commands/000077500000000000000000000000001172670260400211275ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/commands/cases/000077500000000000000000000000001172670260400222255ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/commands/cases/testcases000066400000000000000000000021151172670260400241450ustar00rootroot00000000000000 #### StartPolicy ############################################################### StartPolicy def config(self): self.option["*"] = TELNET_OPT_ACCEPT self.negotiation["1"] = 2 self.negotiation["3"] = TELNET_NEG_NONE EndPolicy IAC=255 SE=240 SB=250 WILL=251 WONT=252 DO=253 DONT=254 SEND=1 IS=0 INFO=2 VAR=0 VALUE=1 ESC=2 USERVAR=3 # Check command 1 /only accept if option 2 negotated / C2P: IAC WILL 2 P2S: IAC WILL 2 S2P: IAC DO 2 P2C: IAC DO 2 C2P: IAC 1 "Egy" P2S: IAC 1 "Egy" . C2P: IAC WILL 1 P2S: IAC WILL 1 S2P: IAC DO 1 P2C: IAC DO 1 C2P: IAC 1 "Ketto" P2S: "Ketto" . C2P: IAC WILL 3 P2S: IAC WILL 3 S2P: IAC DO 3 P2C: IAC DO 3 C2P: IAC 1 "Harom" P2S: "Harom" . # Check command 3. / It's must accept without negotiation / C2P: IAC 3 "Negy" P2S: IAC 3 "Negy" S2P: IAC 3 "Ot" P2C: IAC 3 "Ot" . # Check command 3 / It's accept, when option 3 negotiated / C2P: IAC WILL 4 P2S: IAC WILL 4 S2P: IAC DO 4 P2C: IAC DO 4 C2P: IAC 4 "Hat" P2S: IAC 4 "Hat" S2P: IAC 4 "Het" P2C: "Het" . C2P: IAC WILL 5 P2S: IAC WILL 5 S2P: IAC DO 5 P2C: IAC DO 5 C2P: IAC 4 "Nyolc" P2S: "Nyolc" . zorp-3.9.5/tests/functional/telnet/commands/config000066400000000000000000000000001172670260400223050ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/commands/info000066400000000000000000000000431172670260400220020ustar00rootroot00000000000000Tags: negotiation Min-Version: 3.0 zorp-3.9.5/tests/functional/telnet/func/000077500000000000000000000000001172670260400202615ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/func/cases/000077500000000000000000000000001172670260400213575ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/func/cases/testcases000066400000000000000000000215231172670260400233030ustar00rootroot00000000000000 #### StartPolicy ############################################################### StartPolicy def config(self): TelnetProxyStrict.config(self) EndPolicy #Option negotiation testing # - in which states are options allowed to pass # #Suboption negotiation testing # - null suboption sequence # - too long suboption sequence # - suboption sequence for not accepted option # #Policy logic testing # - DENY, ACCEPT, ABORT # - POLICY IAC=255 SE=240 SB=250 WILL=251 WONT=252 DO=253 DONT=254 SEND=1 IS=0 INFO=2 VAR=0 VALUE=1 ESC=2 USERVAR=3 # ------------------ # general checks # ------------------ # escaped bytes C2P: "alma" IAC IAC DO 1 "korte" P2S: "alma" IAC IAC DO 1 "korte" S2P: "korte" IAC IAC WILL 1 "korte" P2C: "korte" IAC IAC WILL 1 "korte" C2P: "alma" IAC SB 1 "a" IAC SE "korte" P2S: "alma" "korte" S2P: "alma" IAC SB 1 "a" IAC SE "korte" P2C: "alma" "korte" . # ------------------ # overflow checks # ------------------ # too long suboption negotiation C2P: IAC DO 1 P2S: IAC DO 1 S2P: IAC WILL 1 P2C: IAC WILL 1 C2P: "alma" IAC SB 1 "a"x17000 IAC SE "korte" P2S: "alma" P2C: Disconnect . # ------------------ # options # ------------------ C2P: "alma" IAC 1 "korte" P2S: "alma" "korte" . #C2P: IAC DO 1 #P2S: IAC DO 1 #S2P: IAC WILL 1 #P2C: IAC WILL 1 #C2P: "alma" IAC SB 1 "aa" IAC SE "korte" #P2S: "alma" IAC SB 1 "aa" IAC SE "korte" # #C2P: IAC DO 1 #P2S: IAC DO 1 #S2P: IAC WONT 1 #P2C: IAC WONT 1 #C2P: "alma" IAC SB 1 "aa" IAC SE "korte" #P2S: "alma" "korte" # #C2P: IAC DO 1 #P2S: IAC DO 1 #C2P: "alma" IAC SB 1 "aa" IAC SE "korte" #P2S: "alma" "korte" # ------------------ # suboption negotiation # ------------------ # new environment option # ------------------ C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: "alma" IAC SB 39 SEND VAR "USER" VAR "ACCT" IAC SE "korte" P2S: "alma" IAC SB 39 SEND VAR "USER" VAR "ACCT" IAC SE "korte" S2P: "korte" IAC SB 39 IS VAR "USER" VALUE "joe" VAR "ACCT" VALUE "kernel" IAC SE "alma" P2C: "korte" IAC SB 39 IS VAR "USER" VALUE "joe" VAR "ACCT" VALUE "kernel" IAC SE "alma" . C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: "alma" IAC SB 39 8 VAR "USER" VAR "ACCT" IAC SE "korte" P2S: "alma" "korte" . C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: "alma" IAC SB 39 IS VAR "USER" VALUE "joe" IAC SE "korte" P2S: "alma" "korte" . # which side may send IS or SEND C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: "alma" IAC SB 39 IS VAR "USER" VALUE "joe" IAC SE "korte" P2S: "alma" "korte" S2P: "alma" IAC SB 39 SEND IAC SE "korte" P2C: "alma" "korte" . C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: "alma" IAC SB 39 SEND VAR "USER" VAR "ACCT" IAC SE "korte" P2S: "alma" IAC SB 39 SEND VAR "USER" VAR "ACCT" IAC SE "korte" S2P: "alma" IAC SB 39 IS VAR "USER" VALUE "joe" VALUE "err" IAC SE "korte" P2C: "alma" IAC SB 39 IS VAR "USER" VALUE "joe" IAC SE "korte" . # terminal type option # ------------------ C2P: IAC WILL 24 P2S: IAC WILL 24 S2P: IAC DO 24 P2C: IAC DO 24 S2P: "alma" IAC SB 24 SEND IAC SE "korte" P2C: "alma" IAC SB 24 SEND IAC SE "korte" C2P: "alma" IAC SB 24 IS "xterm" IAC SE "korte" P2S: "alma" IAC SB 24 IS "xterm" IAC SE "korte" . C2P: IAC WILL 24 P2S: IAC WILL 24 S2P: IAC DO 24 P2C: IAC DO 24 S2P: "alma" IAC SB 24 8 IAC SE "korte" P2C: "alma" "korte" . C2P: IAC WILL 24 P2S: IAC WILL 24 S2P: IAC DO 24 P2C: IAC DO 24 S2P: "alma" IAC SB 24 SEND IAC SE "korte" P2C: "alma" IAC SB 24 SEND IAC SE "korte" C2P: "alma" IAC SB 24 IS "//" IAC SE "korte" P2S: "alma" "korte" . # which side may send IS or SEND C2P: IAC WILL 24 P2S: IAC WILL 24 S2P: IAC DO 24 P2C: IAC DO 24 S2P: "alma" IAC SB 24 IS "xterm" IAC SE "korte" P2C: "alma" "korte" C2P: "alma" IAC SB 24 SEND IAC SE "korte" P2S: "alma" "korte" . C2P: IAC WILL 24 P2S: IAC WILL 24 S2P: IAC DO 24 P2C: IAC DO 24 S2P: "alma" IAC SB 24 SEND IAC SE "korte" P2C: "alma" IAC SB 24 SEND IAC SE "korte" C2P: "alma" IAC SB 24 IS IAC SE "korte" P2S: "alma" IAC SB 24 IS IAC SE "korte" . # terminal speed # ------------------ C2P: IAC WILL 32 P2S: IAC WILL 32 S2P: IAC DO 32 P2C: IAC DO 32 S2P: "alma" IAC SB 32 SEND IAC SE "korte" P2C: "alma" IAC SB 32 SEND IAC SE "korte" C2P: "alma" IAC SB 32 IS "1200,1200" IAC SE "korte" P2S: "alma" IAC SB 32 IS "1200,1200" IAC SE "korte" . C2P: IAC WILL 32 P2S: IAC WILL 32 S2P: IAC DO 32 P2C: IAC DO 32 S2P: "alma" IAC SB 32 8 IAC SE "korte" P2C: "alma" "korte" . C2P: IAC WILL 32 P2S: IAC WILL 32 S2P: IAC DO 32 P2C: IAC DO 32 S2P: "alma" IAC SB 32 SEND IAC SE "korte" P2C: "alma" IAC SB 32 SEND IAC SE "korte" C2P: "alma" IAC SB 32 IS "aa" IAC SE "korte" P2S: "alma" "korte" . # which side may send IS or SEND C2P: IAC WILL 32 P2S: IAC WILL 32 S2P: IAC DO 32 P2C: IAC DO 32 S2P: "alma" IAC SB 32 IS "1300,1300" IAC SE "korte" P2C: "alma" "korte" C2P: "alma" IAC SB 32 SEND IAC SE "korte" P2S: "alma" "korte" . C2P: IAC WILL 32 P2S: IAC WILL 32 S2P: IAC DO 32 P2C: IAC DO 32 S2P: "alma" IAC SB 32 SEND IAC SE "korte" P2C: "alma" IAC SB 32 SEND IAC SE "korte" C2P: "alma" IAC SB 32 IS IAC SE "korte" P2S: "alma" IAC SB 32 IS IAC SE "korte" . # X display location # ------------------ C2P: IAC WILL 35 P2S: IAC WILL 35 S2P: IAC DO 35 P2C: IAC DO 35 S2P: "alma" IAC SB 35 SEND IAC SE "korte" P2C: "alma" IAC SB 35 SEND IAC SE "korte" C2P: "alma" IAC SB 35 IS "thorin:0" IAC SE "korte" P2S: "alma" IAC SB 35 IS "thorin:0" IAC SE "korte" . C2P: IAC WILL 35 P2S: IAC WILL 35 S2P: IAC DO 35 P2C: IAC DO 35 S2P: "alma" IAC SB 32 8 IAC SE "korte" P2C: "alma" "korte" . C2P: IAC WILL 35 P2S: IAC WILL 35 S2P: IAC DO 35 P2C: IAC DO 35 S2P: "alma" IAC SB 35 SEND IAC SE "korte" P2C: "alma" IAC SB 35 SEND IAC SE "korte" C2P: "alma" IAC SB 35 IS "!dead" IAC SE "korte" P2S: "alma" "korte" . # which side may send IS or SEND C2P: IAC WILL 35 P2S: IAC WILL 35 S2P: IAC DO 35 P2C: IAC DO 35 S2P: "alma" IAC SB 35 IS "thorin:0" IAC SE "korte" P2C: "alma" "korte" C2P: "alma" IAC SB 35 SEND IAC SE "korte" P2S: "alma" "korte" . C2P: IAC WILL 35 P2S: IAC WILL 35 S2P: IAC DO 35 P2C: IAC DO 35 S2P: "alma" IAC SB 35 SEND IAC SE "korte" P2C: "alma" IAC SB 35 SEND IAC SE "korte" C2P: "alma" IAC SB 35 IS IAC SE "korte" P2S: "alma" IAC SB 35 IS IAC SE "korte" . # NAWS # ------------------ C2P: IAC WILL 31 P2S: IAC WILL 31 S2P: IAC DO 31 P2C: IAC DO 31 C2P: "alma" IAC SB 31 00 80 00 25 IAC SE "korte" P2S: "alma" IAC SB 31 00 80 00 25 IAC SE "korte" . C2P: IAC WILL 31 P2S: IAC WILL 31 S2P: IAC DO 31 P2C: IAC DO 31 C2P: "alma" IAC SB 31 00 255 255 00 25 IAC SE "korte" P2S: "alma" IAC SB 31 00 255 255 00 25 IAC SE "korte" . C2P: IAC WILL 31 P2S: IAC WILL 31 S2P: IAC DO 31 P2C: IAC DO 31 C2P: "alma" IAC SB 31 12 34 IAC SE "korte" P2S: "alma" "korte" . # which side may send IS or SEND C2P: IAC WILL 31 P2S: IAC WILL 31 S2P: IAC DO 31 P2C: IAC DO 31 S2P: "alma" IAC SB 31 00 80 00 25 IAC SE "korte" P2C: "alma" "korte" . # ------------------ # policy # ------------------ # these testcases should be run using the following policy # class MyTelnetProxy(TelnetProxy): #### StartPolicy ############################################################### StartPolicy def config(self): self.option[TELNET_ENVIRONMENT, TELNET_SB_IS] = (TELNET_OPT_POLICY, self.rewriteVar) self.option[TELNET_ENVIRONMENT, TELNET_SB_SEND] = TELNET_OPT_ACCEPT self.option[TELNET_TERMINAL_TYPE] = TELNET_OPT_ABORT self.option[TELNET_TERMINAL_SPEED] = TELNET_OPT_DROP self.option[TELNET_ENVIRONMENT] = TELNET_OPT_ACCEPT self.option["101"] = TELNET_OPT_ACCEPT self.option["101", "*"] = TELNET_OPT_DROP self.option["101", "1"] = TELNET_OPT_ACCEPT self.option["101", "2"] = TELNET_OPT_ABORT self.option["102"] = TELNET_OPT_REJECT def rewriteVar(self, option, name, value): if name == "DISPLAY": self.current_var_value = "rewritten:0" return TELNET_OPT_ACCEPT EndPolicy # policy accept C2P: IAC DO 101 P2S: IAC DO 101 S2P: IAC WILL 101 P2C: IAC WILL 101 C2P: "korte" IAC SB 101 1 "Nessa" IAC SE "alma" P2S: "korte" IAC SB 101 1 "Nessa" IAC SE "alma" S2P: "korte" IAC SB 101 1 "Nienna" IAC SE "alma" P2C: "korte" IAC SB 101 1 "Nienna" IAC SE "alma" #policy SB DENY C2P: "korte" IAC SB 101 "Nessa" IAC SE "alma" P2S: "korte" "alma" S2P: "korte" IAC SB 101 "Nienna" IAC SE "alma" P2C: "korte" "alma" . #policy SB REJECT C2P: IAC DO 102 P2C: IAC WONT 102 P2S: IAC DONT 102 S2P: IAC WILL 102 P2S: IAC DONT 102 P2C: IAC WONT 102 . # policy deny C2P: IAC WILL 32 S2P: IAC DO 32 . # policy abort (these should abort the session) C2P: IAC WILL 24 P2C: Disconnect . S2P: IAC WILL 24 P2S: Disconnect . C2P: IAC WONT 24 P2C: Disconnect . S2P: IAC WONT 24 P2S: Disconnect . C2P: IAC DO 24 P2C: Disconnect . S2P: IAC DO 24 P2S: Disconnect . C2P: IAC DONT 24 P2C: Disconnect . S2P: IAC DONT 24 P2S: Disconnect . # policy rewrite C2P: IAC DO 39 P2S: IAC DO 39 S2P: IAC WILL 39 P2C: IAC WILL 39 C2P: IAC SB 39 SEND VAR "DISPLAY" IAC SE P2S: IAC SB 39 SEND VAR "DISPLAY" IAC SE S2P: IAC SB 39 IS VAR "DISPLAY" VALUE "durin:0" IAC SE P2C: IAC SB 39 IS VAR "DISPLAY" VALUE "rewritten:0" IAC SE . zorp-3.9.5/tests/functional/telnet/func/config000066400000000000000000000000001172670260400214370ustar00rootroot00000000000000zorp-3.9.5/tests/functional/telnet/info000066400000000000000000000000361172670260400202030ustar00rootroot00000000000000Base-Class: TelnetProxyStrict zorp-3.9.5/tests/functional/whois/000077500000000000000000000000001172670260400171645ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/attr/000077500000000000000000000000001172670260400201365ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/attr/cases/000077500000000000000000000000001172670260400212345ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/attr/cases/testcases000066400000000000000000000026401172670260400231570ustar00rootroot00000000000000StartPolicy def config(self): WhoisProxy.config(self) self.timeout = 2000 self.max_line_length = 10 EndPolicy # Timeout check moved to timeout.tests C2P: "12345678\r\n" P2S: "12345678\r\n" . C2P: "123456789\r\n" P2C: "Whois protocol error or disallowed protocol element, request denied.\r\n" . C2P: "12345678\r\n" P2S: "12345678\r\n" S2P: "12345678\r\n" P2C: "12345678\r\n" . C2P: "12345678\r\n" P2S: "12345678\r\n" S2P: "123456789\r\n" P2S: Disconnect P2C: Disconnect . StartPolicy def config(self): WhoisProxy.config(self) self.max_request_length = 10 EndPolicy C2P: "1234567890\r\n" P2S: "1234567890\r\n" . C2P: "12345678901\r\n" P2C: "Whois protocol error or disallowed protocol element, request denied.\r\n" . C2P: "1234567890\r\n" P2S: "1234567890\r\n" S2P: "1234567890\r\n" P2C: "1234567890\r\n" . C2P: "1234567890\r\n" P2S: "1234567890\r\n" S2P: "12345678901\r\n" P2C: "12345678901\r\n" . StartPolicy def config(self): WhoisProxy.config(self) def whoisRequest(self, request): if self.request == "minta": return Z_ACCEPT else: return Z_REJECT EndPolicy C2P: "minta\r\n" P2S: "minta\r\n" . C2P: "nem minta\r\n" P2C: "Policy violation, request denied.\r\n" . StartPolicy def config(self): WhoisProxy.config(self) self.response_header = "eleje" self.response_footer = "vege" EndPolicy C2P: "request\r\n" P2S: "request\r\n" S2P: "response\r\n" S2P: Disconnect P2C: "elejeresponse\r\nvege" . zorp-3.9.5/tests/functional/whois/attr/cases/timeout.tests000066400000000000000000000006411172670260400240070ustar00rootroot00000000000000StartGlobalInfo Tags timeout D-01046 bug12123 EndGlobalInfo StartPolicy def config(self): WhoisProxy.config(self) self.timeout = 2000 self.max_line_length = 10 EndPolicy C2P: "keres\r\n" P2S: "keres\r\n" A= time.sleep(3) S2P: "barmi\r\n" . C2P: "keres" A= time.sleep(3) C2P: "\r\n" #P2S: "keres\r\n" P2C: "Whois protocol error or disallowed protocol element, request denied.\r\n" . zorp-3.9.5/tests/functional/whois/attr/config000066400000000000000000000000001172670260400213140ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/func/000077500000000000000000000000001172670260400201175ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/func/cases/000077500000000000000000000000001172670260400212155ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/func/cases/testcases000066400000000000000000000014571172670260400231450ustar00rootroot00000000000000StartPolicy def config(self): WhoisProxy.config(self) EndPolicy C2P: "dump domain netsol.com\r\n" P2S: "dump domain netsol.com\r\n" S2P: "response\r\n" P2C: "response\r\n" . C2P: "?\r\n" P2S: "?\r\n" S2P: "list\r\n" P2C: "list\r\n" . C2P: "\r\n" P2S: "\r\n" S2P: "response\r\n" P2C: "response\r\n" . C2P: "arin.net.134.48.4.129\r\n" P2S: "arin.net.134.48.4.129\r\n" S2P: "response\r\n" P2C: "response\r\n" . C2P: "valami@valami\r\n" P2S: "valami@valami\r\n" S2P: "response\r\n" P2C: "response\r\n" . C2P: "~!@#$%^&*()_+{}|:?>\r\n" P2S: "~!@#$%^&*()_+{}|:?>\r\n" S2P: "response\r\n" P2C: "response\r\n" . C2P: "a"x1024 "\r\n" P2C: "Whois protocol error or disallowed protocol element, request denied.\r\n" . C2P: "keres\r\n" P2S: "keres\r\n" S2P: "a"x1024 "\r\n" P2C: Disconnect . zorp-3.9.5/tests/functional/whois/func/config000066400000000000000000000000001172670260400212750ustar00rootroot00000000000000zorp-3.9.5/tests/functional/whois/info000066400000000000000000000000271172670260400200410ustar00rootroot00000000000000Base-Class: WhoisProxy zorp-3.9.5/tests/functional/whois/spec000066400000000000000000000000531172670260400200370ustar00rootroot00000000000000StartSpec Proxy WhoisProxy EndSpec zorp-3.9.5/tests/kzorp/000077500000000000000000000000001172670260400150365ustar00rootroot00000000000000zorp-3.9.5/tests/kzorp/Makefile.am000066400000000000000000000003071172670260400170720ustar00rootroot00000000000000pkglibdir=$(libdir)/zorp/tests check_PROGRAMS = get_kzorp_result pkglib_PROGRAMS = get_kzorp_result get_kzorp_result_SOURCES = get_kzorp_result.c EXTRA_DIST = test_kzorp_sockopt.py test_kzorp.py zorp-3.9.5/tests/kzorp/get_kzorp_result.c000066400000000000000000000050561172670260400206120ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #define PORT 12345 #define KZ_ATTR_NAME_MAX_LENGTH 1023 #define IP_TRANSPARENT 19 #define SO_KZORP_RESULT 1678333 struct kz_lookup_result { u_int64_t cookie; char czone_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; char szone_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; char dispatcher_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; char service_name[KZ_ATTR_NAME_MAX_LENGTH + 1]; }; int make_socket (uint16_t port) { int sock, flag = 1; struct sockaddr_in name; /* Create the socket. */ sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } /* Set the reuse flag. */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) { perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); exit(EXIT_FAILURE); } /* Set the transparent flag. */ if (setsockopt(sock, SOL_IP, IP_TRANSPARENT, &flag, sizeof(flag)) < 0) { perror("setsockopt(SOL_IP, IP_TRANSPARENT)"); exit(EXIT_FAILURE); } /* Give the socket a name. */ name.sin_family = AF_INET; name.sin_port = htons(port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock, (struct sockaddr *) &name, sizeof (name)) < 0) { perror("bind"); exit(EXIT_FAILURE); } return sock; } void print_kzorp_result(int sock) { struct kz_lookup_result buf; socklen_t size; size = sizeof(buf); if (getsockopt(sock, SOL_IP, SO_KZORP_RESULT, &buf, &size) < 0) { perror("getsockopt(SOL_IP, SO_KZORP_RESULT)"); exit(EXIT_FAILURE); } else { fprintf(stderr, "Cookie: %" PRIu64 ", client zone: '%s', server zone: '%s', dispatcher: '%s', service: '%s'\n", buf.cookie, buf.czone_name, buf.szone_name, buf.dispatcher_name, buf.service_name); } } int main(void) { int sock, new; struct sockaddr_in clientname; socklen_t size; sock = make_socket(PORT); if (listen(sock, 1) < 0) { perror("listen"); exit(EXIT_FAILURE); } fprintf(stderr, "Listening on port %d\n", PORT); size = sizeof(clientname); new = accept(sock, (struct sockaddr *) &clientname, &size); if (new < 0) { perror("accept"); exit(EXIT_FAILURE); } fprintf(stderr, "Connect from %s:%hu\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port)); print_kzorp_result(new); close(new); close(sock); exit(EXIT_SUCCESS); } zorp-3.9.5/tests/kzorp/test_kzorp.py000066400000000000000000004470241172670260400176270ustar00rootroot00000000000000#!/usr/bin/env python import os import errno import string import struct import socket import glob import optparse import sys import types from kznf.kznfnetlink import * from kznf.nfnetlink import * from kznf import * import unittest from Zorp.Zone import InetZone from Zorp.Subnet import InetDomain, Inet6Subnet, InetSubnet FALSE = 0 TRUE = 1 def update_dict(d, **kwargs): ret = d.copy() ret.update(kwargs) return ret def inet_ntoa(a): return "%s.%s.%s.%s" % ((a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff) def inet_aton(a): r = 0L for n in a.split("."): r = (r << 8) + int(n) return r def size_to_mask(family, size): if family == socket.AF_INET: max_size = 32 elif family == socket.AF_INET6: max_size = 128 else: raise ValueError, "address family not supported; family='%d'" % family if size > max_size: raise ValueError, "network size is greater than the maximal size; size='%d', max_size='%d'" % (size, max_size) packed_mask = '' actual_size = 0 while actual_size + 8 < size: packed_mask += '\xff' actual_size = actual_size + 8 if actual_size <= size: packed_mask += chr((0xff << (8 - (size - actual_size))) & 0xff) actual_size = actual_size + 8 while actual_size < max_size: packed_mask += '\x00' actual_size = actual_size + 8 return socket.inet_ntop(family, packed_mask) def curry(fn, *cargs, **ckwargs): def call_fn(*fargs, **fkwargs): d = ckwargs.copy() d.update(fkwargs) return fn(*(cargs + fargs), **d) return call_fn def compose(f, g): return lambda *args, **kwargs: f(g(*args, **kwargs)) class KZorpComm(unittest.TestCase): handle = None _flushables = [ KZNL_MSG_FLUSH_ZONE, KZNL_MSG_FLUSH_SERVICE, KZNL_MSG_FLUSH_DISPATCHER, KZNL_MSG_FLUSH_BIND ] def __init__(self, *args): unittest.TestCase.__init__(self, *args) self.create_handle() self._in_transaction = False self.flush_all() def __del__(self): self.close_handle() def create_handle(self): if self.handle == None: self.handle = kznfnetlink.Handle() self.assertNotEqual(self.handle, None) self.handle.register_subsystem(Subsystem(NFNL_SUBSYS_KZORP)) def close_handle(self): if self.handle: self.handle.close() self.handle = None def reopen_handle(self): self.close_handle() self.create_handle() self._in_transaction = False def send_message(self, message_type, message, assert_on_error = True, message_handler = None, dump = False, error_handler=None): self.assertNotEqual(message, None) self.create_handle() #FIXME: it is just a workaround of a KZrop python module bug message_flags = NLM_F_REQUEST if dump == True: message_flags |= NLM_F_DUMP else: message_flags |= NLM_F_ACK kzorp_message = self.handle.create_message(NFNL_SUBSYS_KZORP, message_type, message_flags) kzorp_message.set_nfmessage(message) res = self.handle.talk(kzorp_message, (0, 0), message_handler) if assert_on_error: #FIXME: positive values should mean error if error_handler is None: self.assertTrue(res >= 0, ("talk with KZorp failed: result='%d' error='%s'" % (res, os.strerror(-res)))) else: error_handler(res) return res def start_transaction(self, instance_name = KZ_INSTANCE_GLOBAL, cookie = 0L): self.send_message(KZNL_MSG_START, create_start_msg(instance_name, cookie)) self._in_transaction = True def end_transaction(self, instance_name = KZ_INSTANCE_GLOBAL): self.send_message(KZNL_MSG_COMMIT, create_commit_msg()) self._in_transaction = False def flush_all(self): if self._in_transaction: self.reopen_handle() self._in_transaction = False for message_type in self._flushables: self.start_transaction() self.send_message(message_type, create_flush_msg()) self.end_transaction() class KZorpBaseTestCaseZones(KZorpComm): _dumped_zones = [] def _dump_zone_handler(self, message): self._dumped_zones.append(message) def check_zone_num(self, num_zones = 0, in_transaction = True): self._dumped_zones = [] if in_transaction == True: self.start_transaction() self.send_message(KZNL_MSG_GET_ZONE, create_get_zone_msg(None), message_handler = self._dump_zone_handler, dump = True) if in_transaction == True: self.end_transaction() self.assertEqual(num_zones, len(self._dumped_zones)) def get_zone_attrs(self, message): self.assertEqual (message.type & 0xff, KZNL_MSG_ADD_ZONE) attrs = message.get_nfmessage().get_attributes() self.assertEqual(attrs.has_key(KZA_ZONE_PARAMS), True) return attrs def get_zone_name(self, message): attrs = self.get_zone_attrs(message) if attrs.has_key(KZA_ZONE_NAME) == True: return parse_name_attr(attrs[KZA_ZONE_NAME]) return None def get_zone_uname(self, message): attrs = self.get_zone_attrs(message) self.assertEqual(attrs.has_key(KZA_ZONE_PARAMS), True) self.assertEqual(attrs.has_key(KZA_ZONE_UNAME), True) return parse_name_attr(attrs[KZA_ZONE_UNAME]) def get_zone_range(self, message): attrs = self.get_zone_attrs(message) self.assertEqual(attrs.has_key(KZA_ZONE_RANGE), True) (family, addr, mask) = parse_inet_range_attr(attrs[KZA_ZONE_RANGE]) return "%s/%s" % (socket.inet_ntop(family, addr), socket.inet_ntop(family, mask)) def send_add_zone_message(self, inet_zone): for m in inet_zone.buildKZorpMessage(): self.send_message(m[0], m[1]) def _check_zone_params(self, add_zone_message, zone_data): self.assertEqual(self.get_zone_name(add_zone_message), zone_data['name']) self.assertEqual(self.get_zone_uname(add_zone_message), zone_data['uname']) attrs = self.get_zone_attrs(add_zone_message) self.assertEqual(zone_data['flags'], parse_int32_attr(attrs[KZA_ZONE_PARAMS])) family = zone_data['family'] self.assertEqual(self.get_zone_range(add_zone_message), "%s/%s" % (zone_data['address'], size_to_mask(family, zone_data['mask']))) class KZorpTestCaseZones(KZorpBaseTestCaseZones): _zones = [ {'name' : 'a', 'uname' : 'root', 'pname' : None, 'address' : '10.0.100.1', 'mask' : 32, 'flags' : KZF_ZONE_UMBRELLA, 'family' : socket.AF_INET}, {'name' : 'b', 'uname' : 'b', 'pname' : 'root', 'address' : '10.0.102.1', 'mask' : 31, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'c', 'uname' : 'c', 'pname' : 'b', 'address' : '10.0.103.1', 'mask' : 30, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'd', 'uname' : 'd', 'pname' : 'b', 'address' : '10.0.104.1', 'mask' : 29, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'e', 'uname' : 'e', 'pname' : 'b', 'address' : '10.0.105.1', 'mask' : 28, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'f', 'uname' : 'f', 'pname' : 'b', 'address' : '10.0.106.1', 'mask' : 27, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'g', 'uname' : 'g', 'pname' : 'f', 'address' : '10.0.107.1', 'mask' : 26, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'h', 'uname' : 'h', 'pname' : 'g', 'address' : '10.0.108.1', 'mask' : 25, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'i', 'uname' : 'i', 'pname' : 'g', 'address' : '10.0.109.1', 'mask' : 24, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'j', 'uname' : 'j', 'pname' : 'g', 'address' : '10.0.110.1', 'mask' : 23, 'flags' : 0, 'family' : socket.AF_INET}, {'name' : 'a6', 'uname' : 'k6', 'pname' : None, 'address' : 'fc00:0:101:1::', 'mask' : 64, 'flags' : 0, 'family' : socket.AF_INET6}, ] def setUp(self): self.start_transaction() for zone in self._zones: family = zone['family'] add_zone_message = create_add_zone_msg(zone['name'], zone['flags'], \ family = family, \ uname = zone['uname'], \ pname = zone['pname'], \ address = socket.inet_pton(family, zone['address']), \ mask = socket.inet_pton(family, size_to_mask(family, zone['mask']))) self.send_message(KZNL_MSG_ADD_ZONE, add_zone_message) self.end_transaction() self._index = -1 self._add_zone_message = None self._add_zone_messages = [] def tearDown(self): self.flush_all() def test_add_zone(self): #set up and ter down test the zone addition self.check_zone_num(len(self._zones)) def test_add_zone_errors(self): zones = [ {'name' : 'fake', 'uname' : 'x0', 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0x2, 'family' : socket.AF_INET, 'error' : -errno.EINVAL}, {'name' : 'fake', 'uname' : 'x1', 'pname' : 'x', 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : -errno.ENOENT}, {'name' : 'fake', 'uname' : 'a', 'pname' : 'xx', 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : -errno.ENOENT}, {'name' : 'fake', 'uname' : 'a', 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : 0}, {'name' : 'fake', 'uname' : 'a', 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : -errno.EEXIST}, {'name' : 'fake', 'uname' : None, 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : 0}, {'name' : 'fake', 'uname' : 'x2', 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : 0}, {'name' : '', 'uname' : 'x3', 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : -errno.EINVAL}, {'name' : 'fake', 'uname' : '', 'pname' : None, 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : -errno.EINVAL}, {'name' : 'fake', 'uname' : None, 'pname' : '', 'address' : None, 'mask' : None, 'flags' : 0, 'family' : socket.AF_INET, 'error' : -errno.EINVAL}, ] add_zone_message = create_add_zone_msg('a', 0); res = self.send_message(KZNL_MSG_ADD_ZONE, add_zone_message, assert_on_error = False) self.assertEqual(res, -errno.ENOENT) self.start_transaction() for zone in zones: mask = zone['mask'] if mask != None: mask = size_to_mask(mask) if zone['address'] != None: add_zone_message = create_add_zone_msg(zone['name'], zone['flags'], \ family = zone['family'], \ uname = zone['uname'], \ pname = zone['pname'], \ address = inet_aton(zone['address']), \ mask = mask) else: add_zone_message = create_add_zone_msg(zone['name'], zone['flags'], \ family = zone['family'], \ uname = zone['uname'], \ pname = zone['pname']) res = self.send_message(KZNL_MSG_ADD_ZONE, add_zone_message, assert_on_error = False) self.assertEqual(res, zone['error']) self.end_transaction() def _get_zone_message_handler(self, msg): self._add_zone_message = msg self._index += 1 self._check_zone_params(msg, self._zones[self._index]) def test_get_zone_by_name(self): #get each created zone for zone in self._zones: zone_name = zone['uname'] self.send_message(KZNL_MSG_GET_ZONE, create_get_zone_msg(zone_name), message_handler = self._get_zone_message_handler) self.assertNotEqual(self._index, len(self._zones)) #get a not existent zone self.assertNotEqual(self._zones[0]['name'], self._zones[0]['uname']) res = self.send_message(KZNL_MSG_GET_ZONE, create_get_zone_msg(self._zones[0]['name']), assert_on_error = False) self.assertEqual(res, -errno.ENOENT) def _get_zones_message_handler(self, msg): self._add_zone_messages.append(msg) def test_get_zone_with_dump(self): #get the dump of zones self.send_message(KZNL_MSG_GET_ZONE, create_get_zone_msg(None), message_handler = self._get_zones_message_handler, dump = True) self.assertEqual(len(self._add_zone_messages), len(self._zones)) for add_zone_message in self._add_zone_messages: for i in range(len(self._zones)): if self.get_zone_uname(add_zone_message) == self._zones[i]['uname']: self._check_zone_params(add_zone_message, self._zones[i]) break else: self.assert_(True, "zone with name %s could not find in the dump" % self.get_zone_uname(add_zone_message)) attrmap = { KZA_SVC_NAME: (create_name_attr, parse_name_attr), KZA_SVC_PARAMS: (create_service_params_attr, parse_service_params_attr), KZA_SVC_SESSION_CNT: (create_int32_attr, parse_int32_attr), KZA_SVC_ROUTER_DST_ADDR: (create_inet_addr_attr, parse_inet_addr_attr), KZA_SVC_ROUTER_DST_PORT: (create_port_attr, parse_port_attr), KZA_SVC_NAT_SRC: (create_nat_range_attr, parse_nat_range_attr), KZA_SVC_NAT_DST: (create_nat_range_attr, parse_nat_range_attr), KZA_SVC_NAT_MAP: (create_nat_range_attr, parse_nat_range_attr), } def create_attr(type, *attr): return attrmap[type][0](type, *attr) def parse_attr(type, attr): if not attr.has_key(type): return None return attrmap[type][1](attr[type]) def create_message(attrs): m = NfnetlinkMessage(socket.AF_NETLINK, 0, 0) for (type, values) in attrs.items(): m.append_attribute(create_attr(type, *values)) return m def service_get_flags(transparent, forge_addr): flags = 0 if (transparent): flags |= KZF_SVC_TRANSPARENT if (forge_addr): flags |= KZF_SVC_FORGE_ADDR return flags class Message: def __init__(self, type): self.message_type = type self.attributes = {} def addAttribute(self, attr_type, attr_value): self.attributes[attr_type] = attr_value return self def getMessage(self): return create_message(self.attributes) def send(self, comm, handler = None): return comm.send_message(self.message_type, self.getMessage(), message_handler = handler, assert_on_error=False) def __str__(self): return "<%d %s>" % self.message_type, str(self.attributes) class BadType(Exception): pass class BadResponse(Exception): pass class MissingAttributes(Exception): def __init__(self, set1, set2): self.set1 = set1 self.set2 = set2 def __str__(self): return "<%s> != <%s>" % (str(self.set1), str(self.set2)) class Message_Add_Service(Message): def __init__(self, name, params): Message.__init__(self, KZNL_MSG_ADD_SERVICE) self.addAttribute(KZA_SVC_NAME, (name,)) self.addAttribute(KZA_SVC_PARAMS, params) def verify(self, comm): def check_response(r): if (r.type & 0xff != KZNL_MSG_ADD_SERVICE): raise BadType attrs = r.get_nfmessage().get_attributes() if (not (attrs.has_key(KZA_SVC_PARAMS) and attrs.has_key(KZA_SVC_NAME))): raise BadResponse # FIXME: must fix how we store attributes in self.attributes: # they are not directly comparable at the moment because single # values are not stored as tuples, but parse_attr always returns # tuples #set1 = frozenset(tuple([(a[0], (parse_attr(a[0], attrs),) for a in attrs.items() ])) #set2 = frozenset(tuple(self.attributes.items())) # #if set1 != set2: raise MissingAttributes(set1, set2) comm.send_message(KZNL_MSG_GET_SERVICE, create_message({KZA_SVC_NAME: self.attributes[KZA_SVC_NAME]}), message_handler = check_response) class Message_Add_Service_Forward(Message_Add_Service): def __init__(self, name, transparent = True, forge_addr = False): Message_Add_Service.__init__(self, name, (KZ_SVC_FORWARD, service_get_flags(transparent, forge_addr))) class Message_Add_Service_Forward_Nontransparent(Message_Add_Service_Forward): def __init__(self, name, router, forge_addr = False): Message_Add_Service_Forward.__init__(self, name, False, forge_addr) self.attributes[KZA_SVC_ROUTER_DST_ADDR] = router class Message_Add_Service_Proxy(Message_Add_Service): def __init__(self, name, flags = 0, transparent = False, forge_addr = False): Message_Add_Service.__init__(self, name, (KZ_SVC_PROXY, flags)) class KZorpTestCaseServices(KZorpComm): def create_add_service_nat_msg_helper(name, mapping, flags, type): m = create_add_service_nat_msg(name, mapping) m.append_attribute(create_service_params_attr(KZA_SVC_PARAMS, type, flags)) return m def create_add_service_session_cnt(cnt, name): m = create_add_proxyservice_msg(name) m.append_attribute(create_int32_attr(KZA_SVC_SESSION_CNT, cnt)) return m def service_with_attrs(m, attr_type, attr_value): m.append_attribute(create_attr(attr_type, attr_value)) return m services = [ (create_add_proxyservice_msg, { 'name': "test-proxy" }, { KZA_SVC_NAME: "test-proxy", KZA_SVC_PARAMS: (0, KZ_SVC_PROXY), KZA_SVC_SESSION_CNT: 0 }), (create_add_pfservice_msg, { 'name': "test-forward", 'flags': KZF_SVC_TRANSPARENT }, { KZA_SVC_NAME: "test-forward", KZA_SVC_PARAMS: (1, KZ_SVC_FORWARD)}), (create_add_pfservice_msg, { 'name': "test3", 'flags': 0, 'dst_family': socket.AF_INET, 'dst_ip': socket.inet_pton(socket.AF_INET, '1.2.3.4'), 'dst_port': 1 }, { KZA_SVC_NAME: "test3", KZA_SVC_PARAMS: (0, KZ_SVC_FORWARD), KZA_SVC_ROUTER_DST_ADDR: socket.inet_pton(socket.AF_INET, '1.2.3.4'), KZA_SVC_ROUTER_DST_PORT: 1 } ), (create_add_pfservice_msg, { 'name': "test6", 'flags': 3, 'dst_family': socket.AF_INET, 'dst_ip': socket.inet_pton(socket.AF_INET, '1.2.3.4'), 'dst_port': 1 }, { KZA_SVC_NAME: "test6", KZA_SVC_PARAMS: (3, KZ_SVC_FORWARD), KZA_SVC_ROUTER_DST_ADDR: None, KZA_SVC_ROUTER_DST_PORT: None } ), #(create_add_service_nat_msg_helper, { 'name': "test4", 'mapping': ((0,2,3,4,5),None,(0,2,3,4,5)), 'type': 1, 'flags': 0 }, { KZA_SVC_NAME: "test4", KZA_SVC_PARAMS: (0, 1) } ), (create_add_service_session_cnt, {'name': 'test5', 'cnt': 303}, { KZA_SVC_NAME: "test5", KZA_SVC_SESSION_CNT: 303 }), ] services3 = [ Message_Add_Service_Proxy("test-proxy").addAttribute(KZA_SVC_SESSION_CNT, (303,)), Message_Add_Service_Forward("test-forward"), Message_Add_Service_Forward_Nontransparent("test-forward-nontransparent", (socket.AF_INET, socket.inet_pton(socket.AF_INET, '1.2.3.4'))).addAttribute(KZA_SVC_ROUTER_DST_PORT, (1025, )), Message_Add_Service_Forward("test-forward-transparent", True, True).addAttribute(KZA_SVC_ROUTER_DST_ADDR, (socket.AF_INET, socket.inet_pton(socket.AF_INET, '1.2.3.4'))).addAttribute(KZA_SVC_ROUTER_DST_PORT, (1025, )) ] def check_svc_num(self, num_svc): _dumped_zones = [] self.send_message(KZNL_MSG_GET_SERVICE, create_get_service_msg(None), message_handler = _dumped_zones.append, dump = True) self.assertEqual(num_svc, len(_dumped_zones)) def print_service(self, m): attrs = m.get_nfmessage().get_attributes() print parse_name_attr(attrs[KZA_SVC_NAME]), attrs.keys() def test_add_message(self): self.start_transaction() res = [ m.send(self) for m in self.services3 ] self.end_transaction() ver = [ m.verify(self) for m in self.services3 ] def test_get_service(self): def check_get_reply(self, service, reply): msg = service[1] reply_attrs = self.get_service_attrs(reply) for (attr_type, value) in service[2].items(): self.assertEqual(parse_attr(attr_type, reply_attrs), service[2][attr_type]) self.check_svc_num(len(self.services)) self.assertEqual(-2, self.send_message(KZNL_MSG_GET_SERVICE, create_get_service_msg("nonexistent"), assert_on_error=False)) for service in self.services: self.send_message(KZNL_MSG_GET_SERVICE, create_get_service_msg(service[1].get('name')), message_handler = curry(check_get_reply, self, service)) def check_send(self, type, message, return_value): self.start_transaction() r = self.send_message(type, message, assert_on_error=False) self.end_transaction() self.assertEqual(return_value, r) def test_add_service_duplicated(self): service_cnt = len(self.services) #duplicated entry check: the matching service was in the same transaction self.start_transaction() self.send_message(KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg("dupe1")) res = self.send_message(KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg("dupe1"), assert_on_error=False) self.end_transaction() self.assertEqual(-errno.EEXIST, res) service_cnt += 1 self.check_svc_num(service_cnt) #duplicated entry check: the matching service was already existing self.check_send(KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg("dupe1"), -errno.EEXIST) self.check_svc_num(service_cnt) def test_add_service_invalid(self): service_cnt = len(self.services) #invalid service type self.check_send(KZNL_MSG_ADD_SERVICE, create_message({KZA_SVC_NAME: ("invalid_service_type", ), KZA_SVC_PARAMS: (KZ_SVC_INVALID, 3) }), -errno.EINVAL) self.check_svc_num(service_cnt) def test_add_service(self): services2 = [ (create_message, {"attrs": {KZA_SVC_NAME: ("test8",), KZA_SVC_PARAMS: (KZ_SVC_PROXY, 0)}}, { KZA_SVC_NAME: "test7" }), ] service_cnt = len(self.services) #outside of transaction self.assertEqual(-errno.ENOENT, self.send_message(KZNL_MSG_ADD_SERVICE, self.services[0][0](**self.services[0][1]), assert_on_error=False)) self.check_svc_num(service_cnt) m = Message_Add_Service_Proxy(KZNL_MSG_ADD_SERVICE, "proba") #FIXME: "TypeError: unsupported operand type(s) for &: 'str' and 'long" #self.start_transaction(KZ_TR_TYPE_SERVICE) #m.send(self) #self.end_transaction() #m.verify(self) def test_add_service_flags(self): service_cnt = len(self.services) for i in xrange(4): self.check_send(KZNL_MSG_ADD_SERVICE, create_message({KZA_SVC_NAME: ("flag-%d" % i,), KZA_SVC_PARAMS: (KZ_SVC_PROXY, i)}), 0) service_cnt += 4 self.check_svc_num(service_cnt) # using undefined flags self.start_transaction() res = self.send_message(KZNL_MSG_ADD_SERVICE, create_message({KZA_SVC_NAME: ("flag-invalid",) , KZA_SVC_PARAMS: (KZ_SVC_PROXY, 0xfffffffc)}), assert_on_error=False) self.end_transaction() self.assertNotEqual(0, res) def test_add_service_nontransparent(self): service_cnt = len(self.services) self.check_send(KZNL_MSG_ADD_SERVICE, create_message({KZA_SVC_NAME: ("test-nontransparent-router",), KZA_SVC_PARAMS: (KZ_SVC_FORWARD, 0), KZA_SVC_ROUTER_DST_ADDR: (socket.AF_INET, socket.inet_pton(socket.AF_INET, '1.2.3.4')), KZA_SVC_ROUTER_DST_PORT: (10010, )}), 0) service_cnt += 1 self.check_svc_num(service_cnt) self.check_send(KZNL_MSG_ADD_SERVICE, create_message({KZA_SVC_NAME: ("test-nontransparent-norouter",), KZA_SVC_PARAMS: (KZ_SVC_FORWARD, 0)}), -errno.EINVAL) self.check_svc_num(service_cnt) def _test_add_service_nat(self, nat_type): service_cnt = len(self.services) self.check_send(nat_type, create_message({KZA_SVC_NAME: ("test-forward",)}), -errno.EINVAL) self.check_send(nat_type, create_message({KZA_SVC_NAME: ("test-forward",), KZA_SVC_NAT_MAP: (1, 12345678, 123456789, 12344, 12345)}), -errno.EINVAL) if (nat_type == KZNL_MSG_ADD_SERVICE_NAT_SRC): self.check_send(nat_type, create_message({KZA_SVC_NAME: ("test-forward",), KZA_SVC_NAT_DST: (1, 12345678, 123456789, 12344, 12345), KZA_SVC_NAT_MAP: (1, 12345678, 123456789, 12344, 12345)}), -errno.EINVAL) self.check_svc_num(service_cnt) #adding a nat rule to a service added in the same transaction self.start_transaction() self.send_message(KZNL_MSG_ADD_SERVICE, create_add_pfservice_msg('test-nat', 1)) self.send_message(nat_type, create_message( { KZA_SVC_NAME: ("test-nat",), KZA_SVC_NAT_SRC: (1, 12345688, 12345689, 1024, 1025), KZA_SVC_NAT_MAP: (1, 12345688, 12345689, 1024, 1025), }), 0) self.end_transaction() service_cnt += 2 self.check_svc_num(service_cnt) def test_add_service_nat_dst(self): self._test_add_service_nat(KZNL_MSG_ADD_SERVICE_NAT_DST) def test_add_service_nat_src(self): self._test_add_service_nat(KZNL_MSG_ADD_SERVICE_NAT_SRC) def setUp(self): self.start_transaction() for service in self.services: self.send_message(KZNL_MSG_ADD_SERVICE, service[0](**service[1])) self.end_transaction() def tearDown(self): self.flush_all(); def get_service_attrs(self, message): self.assertEqual (message.type & 0xff, KZNL_MSG_ADD_SERVICE) attrs = message.get_nfmessage().get_attributes() self.assertEqual(attrs.has_key(KZA_SVC_PARAMS), True) self.assertEqual(attrs.has_key(KZA_SVC_NAME), True) return attrs class KZorpTestCaseFlush(KZorpComm): def __init__(self, *args): KZorpComm.__init__(self, *args) def setUp(self): self._response_message_num = 0 self.start_transaction() message_add_dispatcher = create_add_dispatcher_sabind_msg('dispatcher_name', 0, socket.IPPROTO_TCP, 1000, inet_aton('12.3.4.5'), [(80, 80)]) self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher) self.end_transaction() self.start_transaction() self.send_message(KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg('proxyservice')) self.send_message(KZNL_MSG_ADD_SERVICE, create_add_pfservice_msg('pfservice', KZF_SVC_TRANSPARENT)) self.end_transaction() self.start_transaction() self.send_message(KZNL_MSG_ADD_ZONE, create_add_zone_msg('zone', 0)) self.send_message(KZNL_MSG_ADD_ZONE_SVC_IN, create_add_zone_svc_msg('zone', 'proxyservice')) self.end_transaction() def tearDown(self): self.flush_all() def message_handler(self, message): self._response_message_num += 1 def test_flush_zones(self): self.start_transaction() self.send_message(KZNL_MSG_FLUSH_ZONE, create_flush_msg(), message_handler = self.message_handler) self.end_transaction() self.send_message(KZNL_MSG_GET_ZONE, create_get_zone_msg(None), message_handler = self.message_handler, dump = True) self.assertEqual(self._response_message_num, 0) def test_flush_services(self): self.start_transaction() self.send_message(KZNL_MSG_FLUSH_SERVICE, create_flush_msg(), message_handler = self.message_handler) self.end_transaction() self.send_message(KZNL_MSG_GET_SERVICE, create_get_service_msg(None), message_handler = self.message_handler, dump = True) self.assertEqual(self._response_message_num, 0) def test_flush_dispatchers(self): self.start_transaction() self.send_message(KZNL_MSG_FLUSH_DISPATCHER, create_flush_msg(), message_handler = self.message_handler) self.end_transaction() self.send_message(KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg(None), message_handler = self.message_handler, dump = True) self.assertEqual(self._response_message_num, 0) class KZorpTestCaseTransaction(KZorpBaseTestCaseZones): def setUp(self): self.check_zone_num(0) def tearDown(self): self.flush_all() def test_transactions(self): # Start a transaction self.start_transaction(KZ_INSTANCE_GLOBAL, 123456789L) # Start the transaction again without end transaction message = create_start_msg(KZ_INSTANCE_GLOBAL, 987654321L) res = self.send_message(KZNL_MSG_START, message, False) self.assertEqual(res, -errno.EINVAL) # Commit the transaction without any change self.end_transaction() # Commit the transaction again out of the transaction res = self.send_message(KZNL_MSG_COMMIT, message, False) self.assertEqual(res, -errno.ENOENT) def test_transaction_collision(self): self.start_transaction() message = create_start_msg(KZ_INSTANCE_GLOBAL) res = self.send_message(KZNL_MSG_START, message, False) self.assertEqual(res, -errno.EINVAL) self.end_transaction() def test_transaction_abort(self): self.start_transaction() self.send_message(KZNL_MSG_ADD_ZONE, create_add_zone_msg('zone', 0)) self.end_transaction() self.check_zone_num(1) # Start a transaction self.start_transaction() self.send_message(KZNL_MSG_ADD_ZONE, create_add_zone_msg('a', 0)) self.check_zone_num(1, False) # Abort the transaction self.reopen_handle() self.check_zone_num(1, False) class KZorpBaseTestCaseDispatchers(KZorpComm): _dumped_dispatchers = [] _zones = [ InetZone('internet', ['0.0.0.0/0']), InetZone('A', ['10.99.101.0/25', '10.99.201.0/25'], inbound_services=[ "serv1"]), InetZone('AA', ['10.99.101.0/28', '10.99.201.0/28'], inbound_services=[ "serv1"], admin_parent='A'), InetZone('AAA', ['10.99.101.0/30', '10.99.201.0/30'], inbound_services=[ "serv1"], admin_parent='AA'), InetZone('AAZ', ['10.99.101.4/30', '10.99.201.4/30'], outbound_services=[ "serv1"], admin_parent='AA'), InetZone('AB', ['10.99.101.64/28', '10.99.201.64/28'], inbound_services=[ "serv1"], umbrella=TRUE, admin_parent='A'), InetZone('ABA', ['10.99.101.64/30', '10.99.201.64/30'], inbound_services=[ "serv1"], admin_parent='AB'), InetZone('ABZ', ['10.99.101.68/30', '10.99.201.68/30'], outbound_services=[ "serv1"], admin_parent='AB'), InetZone('AY', ['10.99.101.80/28', '10.99.201.80/28'], outbound_services=[ "serv1"], umbrella=TRUE, admin_parent='A'), InetZone('AYA', ['10.99.101.80/30', '10.99.201.80/30'], inbound_services=[ "serv1"], admin_parent='AY'), InetZone('AYZ', ['10.99.101.84/30', '10.99.201.84/30'], outbound_services=[ "serv1"], admin_parent='AY'), InetZone('AZ', ['10.99.101.16/28', '10.99.201.16/28'], outbound_services=[ "serv1"], admin_parent='A'), InetZone('AZA', ['10.99.101.16/30', '10.99.201.16/30'], inbound_services=[ "serv1"], admin_parent='AZ'), InetZone('AZZ', ['10.99.101.20/30', '10.99.201.20/30'], outbound_services=[ "serv1"], admin_parent='AZ'), InetZone('Z', ['10.99.101.128/25', '10.99.201.128/25'], outbound_services=[ "serv1"]), InetZone('ZA', ['10.99.101.128/28', '10.99.201.128/28'], inbound_services=[ "serv1"], admin_parent='Z'), InetZone('ZAA', ['10.99.101.128/30', '10.99.201.128/30'], inbound_services=[ "serv1"], admin_parent='ZA'), InetZone('ZAZ', ['10.99.101.132/30', '10.99.201.132/30'], outbound_services=[ "serv1"], admin_parent='ZA'), InetZone('ZB', ['10.99.101.192/28', '10.99.201.192/28'], inbound_services=[ "serv1"], umbrella=TRUE, admin_parent='Z'), InetZone('ZBA', ['10.99.101.192/30', '10.99.201.192/30'], inbound_services=[ "serv1"], admin_parent='ZB'), InetZone('ZBZ', ['10.99.101.196/30', '10.99.201.196/30'], outbound_services=[ "serv1"], admin_parent='ZB'), InetZone('ZY', ['10.99.101.208/28', '10.99.201.208/28'], outbound_services=[ "serv1"], umbrella=TRUE, admin_parent='Z'), InetZone('ZYA', ['10.99.101.208/30', '10.99.201.208/30'], inbound_services=[ "serv1"], admin_parent='ZY'), InetZone('ZYZ', ['10.99.101.212/30', '10.99.201.212/30'], outbound_services=[ "serv1"], admin_parent='ZY'), InetZone('ZZ', ['10.99.101.144/28', '10.99.201.144/28'], outbound_services=[ "serv1"], admin_parent='Z'), InetZone('ZZA', ['10.99.101.144/30', '10.99.201.144/30'], inbound_services=[ "serv1"], admin_parent='ZZ'), InetZone('ZZZ', ['10.99.101.148/30', '10.99.201.148/30'], outbound_services=[ "serv1"], admin_parent='ZZ'), ] def _dump_dispatcher_handler(self, message): self._dumped_dispatchers.append(message) def check_dispatcher_num(self, num_dispatchers = 0, in_transaction = True): self._dumped_dispatchers = [] if in_transaction == True: self.start_transaction() self.send_message(KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg(None), message_handler = self._dump_dispatcher_handler, dump = True) if in_transaction == True: self.end_transaction() self.assertEqual(num_dispatchers, len(self._dumped_dispatchers)) def get_dispatcher_attrs(self, message): #self.assertEqual (message.type & 0xff, KZNL_MSG_ADD_DISPATCHER) attrs = message.get_nfmessage().get_attributes() self.assertEqual(attrs.has_key(KZA_DPT_PARAMS), True) return attrs def get_dispatcher_name(self, message): attrs = self.get_dispatcher_attrs(message) if attrs.has_key(KZA_DPT_NAME) == True: return parse_name_attr(attrs[KZA_DPT_NAME]) return None def _check_dispatcher_params(self, add_dispatcher_message, dispatcher_data): self.assertEqual(self.get_dispatcher_name(add_dispatcher_message), dispatcher_data['name']) attrs = self.get_dispatcher_attrs(add_dispatcher_message) flags, proxy_port, dispatcher_type = parse_dispatcher_params_attr(attrs[KZA_DPT_PARAMS]) self.assertEqual(dispatcher_data['flags'], flags) self.assertEqual(dispatcher_data['proxy_port'], proxy_port) self.assertEqual(dispatcher_data['type'], dispatcher_type) if dispatcher_type == KZ_DPT_TYPE_INET: proto, rule_addr, rule_ports = parse_bind_addr_attr(attrs[KZA_DPT_BIND_ADDR]) self.assertEqual(dispatcher_data['rule_addr'], inet_ntoa(rule_addr)) elif dispatcher_type == KZ_DPT_TYPE_IFACE: proto, ifname, rule_ports, pref_addr = parse_bind_iface_attr(attrs[KZA_DPT_BIND_IFACE]) self.assertEqual(dispatcher_data['ifname'], ifname) self.assertEqual(pref_addr, 0) elif dispatcher_type == KZ_DPT_TYPE_IFGROUP: proto, ifgroup, mask, rule_ports, pref_addr = parse_bind_ifgroup_attr(attrs[KZA_DPT_BIND_IFGROUP]) self.assertEqual(dispatcher_data['ifgroup'], ifgroup) self.assertEqual(dispatcher_data['mask'], mask) self.assertEqual(pref_addr, 0) elif dispatcher_type == KZ_DPT_TYPE_N_DIMENSION: num_rules = parse_n_dimension_attr(attrs[KZA_DISPATCHER_N_DIMENSION_PARAMS]) self.assertEqual(dispatcher_data['num_rules'], num_rules) if dispatcher_type == KZ_DPT_TYPE_INET or \ dispatcher_type == KZ_DPT_TYPE_IFACE or \ dispatcher_type == KZ_DPT_TYPE_IFGROUP: self.assertEqual(dispatcher_data['proto'], proto) self.assertEqual(dispatcher_data['rule_ports'], rule_ports) def _check_add_rule_params(self, add_dispatcher_message, rule_data): attrs = add_dispatcher_message.get_nfmessage().get_attributes() rule_id, service, rules = parse_rule_attrs(attrs) self.assertEqual(rule_data['rule_id'], rule_id) self.assertEqual(rule_data['service'], service) self.assertEqual(len(rule_data['entry_nums']), len(rules)) for k, v in rule_data['entry_nums'].items(): self.assertEqual(k in rules, True) self.assertEqual((rule_data['entry_nums'][k],), rules[k]) def _check_add_rule_entry_params(self, add_dispatcher_message, rule_entry_data, rule_entry_index): attrs = add_dispatcher_message.get_nfmessage().get_attributes() rule_id, rule_entries = parse_rule_entry_attrs(attrs) self.assertEqual(rule_entry_data['rule_id'], rule_id) for k, v in rule_entry_data['entry_values'].items(): if rule_entry_data['entry_nums'][k] > rule_entry_index: self.assertEqual(k in rule_entries, True) if k in [KZA_N_DIMENSION_SRC_IP, KZA_N_DIMENSION_DST_IP, KZA_N_DIMENSION_SRC_IP6, KZA_N_DIMENSION_DST_IP6]: (family, addr, mask) = rule_entries[k] self.assertEqual(rule_entry_data['entry_values'][k][rule_entry_index].addr_packed(), addr) self.assertEqual(rule_entry_data['entry_values'][k][rule_entry_index].netmask_packed(), mask) elif k == KZA_N_DIMENSION_SRC_PORT or k == KZA_N_DIMENSION_DST_PORT: self.assertEqual(rule_entry_data['entry_values'][k][rule_entry_index], rule_entries[k]) else: self.assertEqual((rule_entry_data['entry_values'][k][rule_entry_index],), rule_entries[k]) def setup_service_dispatcher(self, services, dispatchers, add_zone = True, add_service = True): self._dumped_diszpancsers = [] if add_zone: self.start_transaction() for zone in self._zones: self.send_add_zone_message(zone) self.end_transaction() if add_service: self.start_transaction() for service in services: if type(service) == types.DictType: service = service['name'] self.send_message(KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg(service)) self.end_transaction() self.start_transaction() for dispatcher in dispatchers: if ('type' in dispatcher) and (dispatcher['type'] != KZ_DPT_TYPE_N_DIMENSION): continue message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, error_handler=lambda res: os.strerror(res)+" "+str(message_add_dispatcher)) for rule in dispatcher['rules']: _max = 0 for name, value in rule['entry_nums'].items(): if _max < value: _max = value message_add_rule = create_add_n_dimension_rule_msg(dispatcher['name'], \ rule['rule_id'], \ rule['service'], \ rule['entry_nums'] \ ) self.send_message(KZNL_MSG_ADD_RULE, message_add_rule) for i in range(_max): data = {} for dim_type in N_DIMENSION_ATTRS: if dim_type in rule['entry_nums'] and rule['entry_nums'][dim_type] > i: if dim_type in [KZA_N_DIMENSION_SRC_IP, KZA_N_DIMENSION_DST_IP, KZA_N_DIMENSION_SRC_IP6, KZA_N_DIMENSION_DST_IP6]: subnet = rule['entry_values'][dim_type][i] data[dim_type] = (subnet.addr_packed(), subnet.netmask_packed()) else: data[dim_type] = rule['entry_values'][dim_type][i] message_add_rule_entry = create_add_n_dimension_rule_entry_msg(dispatcher['name'], rule['rule_id'], data) self.send_message(KZNL_MSG_ADD_RULE_ENTRY, message_add_rule_entry) self.end_transaction() class KZorpTestCaseDispatchers(KZorpBaseTestCaseDispatchers, KZorpBaseTestCaseZones): _all_dispatcher_flags = KZF_SVC_TRANSPARENT | KZF_DPT_FOLLOW_PARENT _dispatchers = [ { 'name' : 'bind_tcp', 'type' : KZ_DPT_TYPE_INET, 'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP, 'proxy_port' : 1, 'rule_ports' : [ (10001, 10001) ], 'rule_addr' : '10.0.0.1'}, { 'name' : 'bind_udp', 'type' : KZ_DPT_TYPE_INET, 'flags' : KZF_DPT_FOLLOW_PARENT, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 10, 'rule_ports' : [ (10002, 10003) ], 'rule_addr' : '10.0.0.1'}, { 'name' : 'bind_flags', 'type' : KZ_DPT_TYPE_INET, 'flags' : _all_dispatcher_flags, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 100, 'rule_ports' : [ (10002, 10003) ], 'rule_addr' : '10.0.0.2'}, { 'name' : 'bind_rule_ports', 'type' : KZ_DPT_TYPE_INET, 'flags' : 0, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 1000, 'rule_ports' : [ (10001, 10001), (10002, 10003), (20004, 30004) ], 'rule_addr' : '10.0.0.3'}, { 'name' : 'ifacebind_tcp', 'type' : KZ_DPT_TYPE_IFACE, 'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP, 'proxy_port' : 1, 'rule_ports' : [ (10001, 10001) ], 'ifname' : 'eth0'}, { 'name' : 'ifacebind_udp', 'type' : KZ_DPT_TYPE_IFACE, 'flags' : KZF_DPT_FOLLOW_PARENT, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 10, 'rule_ports' : [ (10002, 10003) ], 'ifname' : 'eth1'}, { 'name' : 'ifacebind_flags', 'type' : KZ_DPT_TYPE_IFACE, 'flags' : _all_dispatcher_flags, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 100, 'rule_ports' : [ (10002, 10003) ], 'ifname' : 'lo'}, { 'name' : 'ifacebind_rule_ports', 'type' : KZ_DPT_TYPE_IFACE, 'flags' : 0, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 1000, 'rule_ports' : [ (10001, 10001), (10002, 10003), (20004, 30004) ], 'ifname' : 'dummy'}, { 'name' : 'ifgroupbind_tcp', 'type' : KZ_DPT_TYPE_IFGROUP, 'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP, 'proxy_port' : 1, 'rule_ports' : [ (10001, 10001) ], 'ifgroup' : 0, 'mask' : 10}, { 'name' : 'ifgroupbind_udp', 'type' : KZ_DPT_TYPE_IFGROUP, 'flags' : KZF_DPT_FOLLOW_PARENT, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 10, 'rule_ports' : [ (10002, 10003) ], 'ifgroup' : 1, 'mask' : 11}, { 'name' : 'ifgroupbind_flags', 'type' : KZ_DPT_TYPE_IFGROUP, 'flags' : _all_dispatcher_flags, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 100, 'rule_ports' : [ (10002, 10003) ], 'ifgroup' : 2, 'mask' : 12}, { 'name' : 'ifgroupbind_rule_ports', 'type' : KZ_DPT_TYPE_IFGROUP, 'flags' : 0, 'proto' : socket.IPPROTO_UDP, 'proxy_port' : 1000, 'rule_ports' : [ (10001, 10001), (10002, 10003), (20004, 30004) ], 'ifgroup' : 3, 'mask' : 13}, { 'name' : 'n_dimension', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 2, KZA_N_DIMENSION_SRC_ZONE : 2 }, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(12,12), (23, 44)], KZA_N_DIMENSION_SRC_ZONE : ["AAA", "ZZZ"] } } ] }, { 'name' : 'n_dimension_with_rules', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 3, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(5,6)] } }, { 'rule_id' : 2, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2, KZA_N_DIMENSION_DST_PORT : 3 }, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['eth0', 'eth1'], KZA_N_DIMENSION_DST_PORT : [(3,3), (4,4), (50000,65534)]} }, { 'rule_id' : 3, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 1, KZA_N_DIMENSION_SRC_ZONE : 4, KZA_N_DIMENSION_DST_PORT : 2 }, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [(1,2)], KZA_N_DIMENSION_SRC_ZONE : ['AAA', 'AZA', 'AA', 'A'], KZA_N_DIMENSION_DST_PORT : [(10000,10000), (20000, 30000)] } } ] }, { 'name' : 'n_dimension_with_ALL_rules', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2, KZA_N_DIMENSION_PROTO : 1, KZA_N_DIMENSION_SRC_PORT : 2, KZA_N_DIMENSION_DST_PORT : 1, KZA_N_DIMENSION_SRC_IP : 2, KZA_N_DIMENSION_SRC_ZONE : 3, KZA_N_DIMENSION_DST_IP : 2, KZA_N_DIMENSION_DST_ZONE : 1, KZA_N_DIMENSION_IFGROUP : 1}, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['eth4', 'eth2'], KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP], KZA_N_DIMENSION_SRC_PORT : [(2,3), (4,5)], KZA_N_DIMENSION_DST_PORT : [(5,6)], KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.4'), InetDomain('2.3.4.5/24')], KZA_N_DIMENSION_SRC_ZONE : ['ZZZ', 'ZZ', 'Z'], KZA_N_DIMENSION_DST_IP : [InetDomain('3.4.5.6/16'), InetDomain('4.5.6.7/8')], KZA_N_DIMENSION_DST_ZONE : 'AAA', KZA_N_DIMENSION_IFGROUP : [1]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 2, KZA_N_DIMENSION_DST_IP : 3, KZA_N_DIMENSION_SRC_ZONE : 1, KZA_N_DIMENSION_SRC_IP : 2, KZA_N_DIMENSION_DST_PORT : 2, KZA_N_DIMENSION_SRC_PORT : 2, KZA_N_DIMENSION_PROTO : 1, KZA_N_DIMENSION_IFACE : 3 }, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['AZA', 'ZAZ'], KZA_N_DIMENSION_DST_IP : [InetDomain('8.7.6.5'), InetDomain('7.6.5.4/31'), InetDomain('9.8.7.6/25')], KZA_N_DIMENSION_SRC_ZONE : 'ZZ', KZA_N_DIMENSION_SRC_IP : [InetDomain('5.4.3.2/32'), InetDomain('6.5.4.3/30')], KZA_N_DIMENSION_DST_PORT : [(66,66),(100,200)], KZA_N_DIMENSION_SRC_PORT : [(23,24), (30, 40)], KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP], KZA_N_DIMENSION_IFACE : ['eth0', 'eth1', 'eth2'] } } ] } ] _services_tmp = [ {'dispatcher_name' : 'n_dimension', 'name' : 'A_A', 'czone' : 'A', 'szone' : 'A'}, {'dispatcher_name' : 'n_dimension_2', 'name' : 'Z_Z', 'czone' : 'Z', 'szone' : 'Z'} ] def __init__(self, *args): KZorpBaseTestCaseDispatchers.__init__(self, *args) KZorpBaseTestCaseZones.__init__(self, *args) self._add_dispatcher_messages = [] self._add_dispatcher_message = None self._index = -1 def setUp(self): self.setup_service_dispatcher(self._services_tmp, self._dispatchers) self.start_transaction() for dispatcher in self._dispatchers: dispatcher_type = dispatcher['type'] if dispatcher_type == KZ_DPT_TYPE_N_DIMENSION: continue if dispatcher_type == KZ_DPT_TYPE_INET: message_add_dispatcher = create_add_dispatcher_sabind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ inet_aton(dispatcher['rule_addr']), \ dispatcher['rule_ports']) elif dispatcher_type == KZ_DPT_TYPE_IFACE: message_add_dispatcher = create_add_dispatcher_ifacebind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ dispatcher['ifname'], dispatcher['rule_ports']) elif dispatcher_type == KZ_DPT_TYPE_IFGROUP: message_add_dispatcher = create_add_dispatcher_ifgroupbind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ dispatcher['ifgroup'], dispatcher['mask'], dispatcher['rule_ports']) self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher) self.end_transaction() def tearDown(self): self.flush_all() pass def test_get_4k_dispatcher(self): services = ['A_A'] _iface_num = 300 _iface_list = [] _iface_string = "abcdefghijklmno" for i in range(_iface_num): _iface_list.append(_iface_string) dispatchers = [{ 'name' : 'n_dimension_4k', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : _iface_num, KZA_N_DIMENSION_PROTO : 1, KZA_N_DIMENSION_SRC_PORT : 2, KZA_N_DIMENSION_DST_PORT : 1, KZA_N_DIMENSION_SRC_IP : 2, KZA_N_DIMENSION_SRC_ZONE : 3, KZA_N_DIMENSION_DST_IP : 2, KZA_N_DIMENSION_DST_ZONE : 1, KZA_N_DIMENSION_IFGROUP : 1}, 'entry_values' : { KZA_N_DIMENSION_IFACE : _iface_list, KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP], KZA_N_DIMENSION_SRC_PORT : [(2,3), (4,5)], KZA_N_DIMENSION_DST_PORT : [(5,6)], KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.4'), InetDomain('2.3.4.5/24')], KZA_N_DIMENSION_SRC_ZONE : ['ZZZ', 'ZZ', 'Z'], KZA_N_DIMENSION_DST_IP : [InetDomain('3.4.5.6/16'), InetDomain('4.5.6.7/8')], KZA_N_DIMENSION_DST_ZONE : 'AAA', KZA_N_DIMENSION_IFGROUP : [1]}, } ] }] self.setup_service_dispatcher(services, dispatchers, False, False); self.send_message(KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg("n_dimension_4k"), message_handler = self._get_dispatchers_message_handler) self._check_dispatcher_params(self._add_dispatcher_messages[0], dispatchers[0]) self._check_ndim_params(dispatchers) def test_n_dimension_errors(self): error_dup_dispatchers=[ { 'name' : 'n_dimension_error', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 0, }, { 'name' : 'n_dimension_error2', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2}, 'errno' : 0 } ] } ] error_num_rules_dispatchers=[ { 'name' : 'n_dimension_error3', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [{ 'rule_id' : 2, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2}, 'errno' : 0 }, { 'rule_id' : 3, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2}, 'errno' : -errno.EINVAL } ] }, { 'name' : 'n_dimension_error4', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [{ 'rule_id' : 3, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2}, #FIXME: this shouldbe: -errno.EEXIST 'errno' : 0 } ] } ] error_num_rule_entries=[ { 'name' : 'n_dimension_error5', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 8, 'rules' : [{ 'rule_id' : 4, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 1 }, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['eth4', 'eth2'] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 5, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_PROTO : 1 }, 'entry_values' : { KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP, socket.IPPROTO_UDP] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 6, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [(1,1), (2,2)] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 7, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(3,3),(4,5)] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 8, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.4'), InetDomain('2.3.4.5')] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 9, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['ZZZ', 'ZZ'] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 10, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetDomain('3.4.5.6'), InetDomain('4.5.6.7')] }, 'rule_entry_errnos' : [0, -errno.ENOMEM] }, { 'rule_id' : 11, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['AAA', 'AA']}, 'rule_entry_errnos' : [0, -errno.ENOMEM] } ] } ] error_zones_exist=[ { 'name' : 'n_dimension_error6', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 12, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : 'BBB' }, 'rule_entry_errnos' : [-errno.ENOENT] }, { 'rule_id' : 13, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : 'CCC' }, 'rule_entry_errnos' : [-errno.ENOENT] } ] } ] #Check add_dispatcher without starting a transaction dispatcher = error_dup_dispatchers[0] message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) self.assertEqual(res, -errno.ENOENT) #check duplicated add_dispatcher self.start_transaction() message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) self.assertEqual(res, 0) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) self.assertEqual(res, -errno.EEXIST) self.end_transaction() #check if num_rules > number of rule_entries dispathcer = error_dup_dispatchers[1] self.start_transaction() message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) self.assertEqual(res, 0) self.end_transaction() #check if num_rules < number of rule entries, check adding existing rule_id self.start_transaction() for i in range(len(error_num_rules_dispatchers)): dispatcher = error_num_rules_dispatchers[i] dispatcher_type = dispatcher['type'] message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) for rule in dispatcher['rules']: message_add_rule = create_add_n_dimension_rule_msg(dispatcher['name'], \ rule['rule_id'], \ rule['service'], \ rule['entry_nums'] \ ) res = self.send_message(KZNL_MSG_ADD_RULE, message_add_rule, assert_on_error = False) if 'errno' in rule: self.assertEqual(res, rule['errno']) self.end_transaction() #check if entry_nums < number of entry_values self.start_transaction() for i in range(len(error_num_rule_entries)): dispatcher = error_num_rule_entries[i] dispatcher_type = dispatcher['type'] message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) for rule in dispatcher['rules']: _max = 2 message_add_rule = create_add_n_dimension_rule_msg(dispatcher['name'], \ rule['rule_id'], \ rule['service'], \ rule['entry_nums'] \ ) res = self.send_message(KZNL_MSG_ADD_RULE, message_add_rule, assert_on_error = False) if 'errno' in rule: self.assertEqual(res, rule['errno']) for i in range(_max): data = {} for dim_type in N_DIMENSION_ATTRS: if dim_type in rule['entry_nums']: if dim_type in [KZA_N_DIMENSION_SRC_IP, KZA_N_DIMENSION_DST_IP, KZA_N_DIMENSION_SRC_IP6, KZA_N_DIMENSION_DST_IP6]: data[dim_type] = (rule['entry_values'][dim_type][i].addr_packed(), rule['entry_values'][dim_type][i].netmask_packed()) else: data[dim_type] = rule['entry_values'][dim_type][i] message_add_rule_entry = create_add_n_dimension_rule_entry_msg(dispatcher['name'], rule['rule_id'], data) res = self.send_message(KZNL_MSG_ADD_RULE_ENTRY, message_add_rule_entry, assert_on_error = False) self.assertEqual(res, rule['rule_entry_errnos'][i]) self.end_transaction() self.start_transaction() #check zones exist for i in range(len(error_zones_exist)): dispatcher = error_zones_exist[i] dispatcher_type = dispatcher['type'] message_add_dispatcher = create_add_dispatcher_n_dimension(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proxy_port'], \ dispatcher['num_rules'] \ ) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) for rule in dispatcher['rules']: _max = 1 message_add_rule = create_add_n_dimension_rule_msg(dispatcher['name'], \ rule['rule_id'], \ rule['service'], \ rule['entry_nums'] \ ) res = self.send_message(KZNL_MSG_ADD_RULE, message_add_rule, assert_on_error = False) if 'errno' in rule: self.assertEqual(res, rule['errno']) for i in range(_max): data = {} for dim_type in N_DIMENSION_ATTRS: if dim_type in rule['entry_nums']: if dim_type == KZA_N_DIMENSION_SRC_IP or dim_type == KZA_N_DIMENSION_DST_IP: data[dim_type] = (struct.pack('I', rule['entry_values'][dim_type][i].ip), struct.pack('I', rule['entry_values'][dim_type][i].mask)) else: data[dim_type] = rule['entry_values'][dim_type][i] message_add_rule_entry = create_add_n_dimension_rule_entry_msg(dispatcher['name'], rule['rule_id'], data) res = self.send_message(KZNL_MSG_ADD_RULE_ENTRY, message_add_rule_entry, assert_on_error = False) self.assertEqual(res, rule['rule_entry_errnos'][i]) self.end_transaction() pass def test_add_dispatcher(self): #set up and ter down test the dispatcher addition num_rules = 0 num_rule_entries = 0 for dispatcher in self._dispatchers: if dispatcher['type'] == KZ_DPT_TYPE_N_DIMENSION: for rule in dispatcher['rules']: num_rules = num_rules + 1 _max = 0 for name, value in rule['entry_nums'].items(): if _max < value: _max = value num_rule_entries = num_rule_entries + _max self.check_dispatcher_num(num_rules + num_rule_entries + len(self._dispatchers)) def test_add_dispatcher_errors(self): error_dispatchers = [ { 'name' : 'tcp', 'flags' : KZF_DPT_TRANSPARENT, 'proto' : -1, 'proxy_port' : 1, 'rule_ports' : [ [10001, 10001] ], 'rule_addr' : '10.0.0.1', 'error' : -errno.EINVAL}, { 'name' : 'tcp', 'flags' : KZF_DPT_TRANSPARENT, 'proto' : 5, 'proxy_port' : 1, 'rule_ports' : [ [10001, 10001] ], 'rule_addr' : '10.0.0.1', 'error' : -errno.EINVAL}, { 'name' : 'tcp', 'flags' : KZF_DPT_TRANSPARENT, 'proto' : 7, 'proxy_port' : 1, 'rule_ports' : [ [10001, 10001] ], 'rule_addr' : '10.0.0.1', 'error' : -errno.EINVAL}, { 'name' : 'tcp', 'flags' : KZF_DPT_TRANSPARENT, 'proto' : 15, 'proxy_port' : 1, 'rule_ports' : [ [10001, 10001] ], 'rule_addr' : '10.0.0.1', 'error' : -errno.EINVAL}, { 'name' : 'tcp', 'flags' : KZF_DPT_TRANSPARENT, 'proto' : 17, 'proxy_port' : 1, 'rule_ports' : [ [10001, 10001] ], 'rule_addr' : '10.0.0.1', 'error' : -errno.EINVAL}, ] dispatcher = self._dispatchers[0] message_add_dispatcher = create_add_dispatcher_sabind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ inet_aton(dispatcher['rule_addr']), \ dispatcher['rule_ports']) res = self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher, assert_on_error = False) self.assertEqual(res, -errno.ENOENT) #FIXME: unfinished unit test? """ self.start_transaction(KZ_TR_TYPE_DISPATCHER) for dispatcher in self._dispatchers: message_add_dispatcher = create_add_dispatcher_sabind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ inet_aton(dispatcher['rule_addr']), \ dispatcher['rule_ports']) self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher) self.end_transaction() """ def _get_dispatcher_message_handler(self, msg): self._add_dispatcher_message = msg self._index += 1 self._check_dispatcher_params(msg, self._dispatchers[self._index]) def test_get_dispatcher_by_name(self): #get each created dispatcher for dispatcher in self._dispatchers: if dispatcher['type'] == KZ_DPT_TYPE_N_DIMENSION: continue dispatcher_name = dispatcher['name'] self.send_message(KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg(dispatcher_name), message_handler = self._get_dispatcher_message_handler) self.assertNotEqual(self._index, len(self._dispatchers)) #get a not existent dispatcher res = self.send_message(KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg('nonexistentdispatchername'), assert_on_error = False) self.assertEqual(res, -errno.ENOENT) def _get_dispatchers_message_handler(self, msg): self._add_dispatcher_messages.append(msg) def _check_ndim_params(self, dispatchers): rule_entry_dispatcher_name = "" for add_dispatcher_message in self._add_dispatcher_messages: attrs = add_dispatcher_message.get_nfmessage().get_attributes() message_type = add_dispatcher_message.type & 0xff if (message_type == KZNL_MSG_ADD_DISPATCHER or message_type == KZNL_MSG_ADD_RULE): dispatcher_name = parse_name_attr(attrs[KZA_DPT_NAME]) for i in range(len(dispatchers)): if message_type == KZNL_MSG_ADD_DISPATCHER and dispatcher_name == dispatchers[i]['name']: if dispatchers[i]['type'] == KZ_DPT_TYPE_N_DIMENSION: rule_index = 0 self._check_dispatcher_params(add_dispatcher_message, dispatchers[i]) break; elif message_type == KZNL_MSG_ADD_RULE and dispatcher_name == dispatchers[i]['name']: self._check_add_rule_params(add_dispatcher_message, dispatchers[i]['rules'][rule_index]) rule_entry_dispatcher_name = dispatcher_name rule_index = rule_index + 1 rule_entry_index = 0 break; elif message_type == KZNL_MSG_ADD_RULE_ENTRY and dispatchers[i]['name'] == rule_entry_dispatcher_name: self._check_add_rule_entry_params(add_dispatcher_message, dispatchers[i]['rules'][rule_index - 1], rule_entry_index) rule_entry_index = rule_entry_index + 1 break; else: self.assert_(True, "dispatcher with name %s could not find in the dump") #% self.get_dispatcher_name(add_dispatcher_message)) def test_get_dispatcher_with_dump(self): #get the dump of dispatchers self.send_message(KZNL_MSG_GET_DISPATCHER, create_get_dispatcher_msg(None), message_handler = self._get_dispatchers_message_handler, dump = True) self._check_ndim_params(self._dispatchers) #self.assertEqual(len(self._add_dispatcher_messages), len(self._dispatchers)) class KZorpBaseTestCaseQuery(KZorpBaseTestCaseDispatchers, KZorpBaseTestCaseZones): _object_count = 0 def __init__(self, *args): KZorpBaseTestCaseDispatchers.__init__(self, *args) KZorpBaseTestCaseZones.__init__(self, *args) self._initialized = False self._dumped_diszpancsers = [] if (KZorpBaseTestCaseQuery._object_count == 0): self.initialize() KZorpBaseTestCaseQuery._object_count += 1 def __del__(self): KZorpBaseTestCaseQuery._object_count -= 1 if (KZorpBaseTestCaseQuery._object_count == 0): self.deinitialize() def initialize(self): os.system('modprobe dummy numdummies=6') os.system('ifconfig dummy0 10.99.201.1 netmask 255.255.255.0') os.system('ifconfig dummy1 10.99.202.2 netmask 255.255.255.0') os.system('ifconfig dummy2 10.99.203.3 netmask 255.255.255.0') os.system('ifconfig dummy3 10.99.204.4 netmask 255.255.255.0') os.system('ifconfig dummy4 10.99.205.5 netmask 255.255.255.0') os.system('ifconfig dummy5 10.99.205.6 netmask 255.255.255.0') os.system('echo 0x1 > /sys/class/net/dummy3/ifgroup') os.system('echo 0x1 > /sys/class/net/dummy4/ifgroup') def deinitialize(self): os.system('rmmod dummy') def get_dispatcher_attrs(self, message): attrs = message.get_nfmessage().get_attributes() return attrs def get_service_name(self, message): attrs = message.get_nfmessage().get_attributes() return parse_attr(KZA_SVC_NAME, attrs); def get_client_zone_name(self, message): attrs = message.get_nfmessage().get_attributes() client_zone = "not found" if attrs.has_key(KZA_QUERY_CLIENT_ZONE): client_zone = parse_name_attr(attrs[KZA_QUERY_CLIENT_ZONE]) return client_zone def get_server_zone_name(self, message): attrs = message.get_nfmessage().get_attributes() server_zone = "not found" if attrs.has_key(KZA_QUERY_SERVER_ZONE): server_zone = parse_name_attr(attrs[KZA_QUERY_SERVER_ZONE]) return server_zone def _query_message_handler(self, msg): self._dumped_diszpancsers.append(msg) class KZorpTestCaseQuery(KZorpBaseTestCaseQuery): _dispatchers = [ { 'name' : 'tp_sockaddr', 'type' : KZ_DPT_TYPE_INET,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'tp_sockaddr_ports', 'type' : KZ_DPT_TYPE_INET,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'tp_sockaddr_udp', 'type' : KZ_DPT_TYPE_INET,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'tp_sockaddr_ports_udp', 'type' : KZ_DPT_TYPE_INET,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (20001, 10002), (30001, 30003), (40001, 40003) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'non_tp_sockaddr', 'type' : KZ_DPT_TYPE_INET,'flags' : 0, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'non_tp_sockaddr_ports', 'type' : KZ_DPT_TYPE_INET,'flags' : 0, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'non_tp_sockaddr_udp', 'type' : KZ_DPT_TYPE_INET,'flags' : 0, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'non_tp_sockaddr_ports_udp', 'type' : KZ_DPT_TYPE_INET,'flags' : 0, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'tp_iface', 'type' : KZ_DPT_TYPE_IFACE,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifname' : 'dummy1'}, { 'name' : 'tp_iface_ports', 'type' : KZ_DPT_TYPE_IFACE,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifname' : 'dummy1'}, { 'name' : 'tp_iface_udp', 'type' : KZ_DPT_TYPE_IFACE,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifname' : 'dummy1'}, { 'name' : 'tp_iface_ports_udp', 'type' : KZ_DPT_TYPE_IFACE,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifname' : 'dummy1'}, { 'name' : 'non_tp_iface', 'type' : KZ_DPT_TYPE_IFACE,'flags' : 0, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifname' : 'dummy1'}, { 'name' : 'non_tp_iface_ports', 'type' : KZ_DPT_TYPE_IFACE,'flags' : 0, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifname' : 'dummy1'}, { 'name' : 'non_tp_iface_udp', 'type' : KZ_DPT_TYPE_IFACE,'flags' : 0, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifname' : 'dummy1'}, { 'name' : 'non_tp_iface_ports_udp', 'type' : KZ_DPT_TYPE_IFACE,'flags' : 0, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifname' : 'dummy1'}, { 'name' : 'tp_ifgroup', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifgroup' : 1}, { 'name' : 'tp_ifgroup_ports', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifgroup' : 1}, { 'name' : 'tp_ifgroup_udp', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifgroup' : 1}, { 'name' : 'tp_ifgroup_ports_udp', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifgroup' : 1}, { 'name' : 'non_tp_ifgroup', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : 0, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifgroup' : 1}, { 'name' : 'non_tp_ifgroup_ports', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : 0, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifgroup' : 1}, { 'name' : 'non_tp_ifgroup_udp', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : 0, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (10001, 10001) ],'ifgroup' : 1}, { 'name' : 'non_tp_ifgroup_ports_udp', 'type' : KZ_DPT_TYPE_IFGROUP,'flags' : 0, 'proto' : socket.IPPROTO_UDP,'proxy_port' : 1,'rule_ports' : [ (20001, 20001), (30001, 30003), (40001, 40003) ],'ifgroup' : 1} ] _cszone_dispatchers = [ { 'name' : 'css_sockaddr', 'type' : KZ_DPT_TYPE_INET,'flags' : KZF_DPT_TRANSPARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (50001, 50001) ],'rule_addr' : '10.99.201.1'}, { 'name' : 'css_fp_sockaddr', 'type' : KZ_DPT_TYPE_INET,'flags' : KZF_DPT_TRANSPARENT | KZF_DPT_FOLLOW_PARENT, 'proto' : socket.IPPROTO_TCP,'proxy_port' : 1,'rule_ports' : [ (50005, 50005) ],'rule_addr' : '10.99.201.1'} ] _services = [ {'dispatcher_name' : 'css_sockaddr', 'name' : 'AAA_ZAZ', 'czone' : 'AAA', 'szone' : 'ZAZ'}, {'dispatcher_name' : 'css_sockaddr', 'name' : 'AY_ZYA', 'czone' : 'AY', 'szone' : 'ZYA'}, {'dispatcher_name' : 'css_sockaddr', 'name' : 'ABZ_ZBA', 'czone' : 'ABZ', 'szone' : 'ZBA'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AA_ZBZ', 'czone' : 'AA', 'szone' : 'ZBZ'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AYZ_ZB', 'czone' : 'AYZ', 'szone' : 'ZB'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AB_ZZZ', 'czone' : 'AB', 'szone' : 'ZZZ'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AZZ_ZBZ', 'czone' : 'AZZ', 'szone' : 'ZBZ'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AZZ_ZAZ', 'czone' : 'AZZ', 'szone' : 'ZAZ'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AZ_ZYA', 'czone' : 'AZ', 'szone' : 'ZYA'}, {'dispatcher_name' : 'css_fp_sockaddr', 'name' : 'AAZ_ZYZ', 'czone' : 'AAZ', 'szone' : 'ZYZ'} ] def __init__(self, *args): KZorpBaseTestCaseQuery.__init__(self, *args) def setUp(self): self.start_transaction() for zone in self._zones: self.send_add_zone_message(zone) self.end_transaction() self.start_transaction() for dispatcher in self._dispatchers: dispatcher_type = dispatcher['type'] if dispatcher_type == KZ_DPT_TYPE_INET: message_add_dispatcher = create_add_dispatcher_sabind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ inet_aton(dispatcher['rule_addr']), \ dispatcher['rule_ports']) elif dispatcher_type == KZ_DPT_TYPE_IFACE: message_add_dispatcher = create_add_dispatcher_ifacebind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ dispatcher['ifname'], dispatcher['rule_ports']) elif dispatcher_type == KZ_DPT_TYPE_IFGROUP: message_add_dispatcher = create_add_dispatcher_ifgroupbind_msg(dispatcher['name'], \ dispatcher['flags'], \ dispatcher['proto'], \ dispatcher['proxy_port'], \ dispatcher['ifgroup'], 1, dispatcher['rule_ports']) self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher) self.end_transaction() self.start_transaction() for service in self._services: self.send_message(KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg(service['name'])) self.end_transaction() self.start_transaction() for cszone_dispatcher in self._cszone_dispatchers: message_add_dispatcher = create_add_dispatcher_sabind_msg(cszone_dispatcher['name'], \ cszone_dispatcher['flags'], \ cszone_dispatcher['proto'], \ cszone_dispatcher['proxy_port'], \ inet_aton(cszone_dispatcher['rule_addr']), \ cszone_dispatcher['rule_ports']) self.send_message(KZNL_MSG_ADD_DISPATCHER, message_add_dispatcher) for service in self._services: self.send_message(KZNL_MSG_ADD_DISPATCHER_CSS, create_add_dispatcher_css_msg(service['dispatcher_name'], service['name'], service['czone'], service['szone'])) self.end_transaction() def tearDown(self): self.flush_all() def test_dispatcher_query(self): _queries = [ { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '8.4.2.1', 'iface' : 'eth1'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '10.99.201.1', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 40003, 'daddr' : '1.1.1.1', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 40002, 'daddr' : '10.99.201.1', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '8.4.2.1', 'iface' : 'eth1'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '10.99.201.1', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 40003, 'daddr' : '1.1.1.1', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 40002, 'daddr' : '10.99.201.1', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '10.99.202.2', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 20001, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 30002, 'daddr' : '10.99.202.2', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '10.99.202.2', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 20001, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 30002, 'daddr' : '10.99.202.2', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '1.1.1.1', 'iface' : 'dummy3'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '10.99.205.5', 'iface' : 'dummy4'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 20001, 'daddr' : '1.1.1.1', 'iface' : 'dummy4'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 30002, 'daddr' : '10.99.204.4', 'iface' : 'dummy3'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '1.1.1.1', 'iface' : 'dummy3'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 10001, 'daddr' : '10.99.205.5', 'iface' : 'dummy4'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 20001, 'daddr' : '1.1.1.1', 'iface' : 'dummy4'}, { 'proto' : socket.IPPROTO_UDP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '1.2.4.0', 'dport' : 30002, 'daddr' : '10.99.204.4', 'iface' : 'dummy3'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.1' , 'dport' : 50001, 'daddr' : '10.99.201.133', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.94', 'dport' : 50001, 'daddr' : '10.99.201.208', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.69', 'dport' : 50001, 'daddr' : '10.99.201.193', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.5', 'dport' : 50005, 'daddr' : '10.99.201.197', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.85', 'dport' : 50005, 'daddr' : '10.99.201.193', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.65', 'dport' : 50005, 'daddr' : '10.99.201.149', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.21', 'dport' : 50005, 'daddr' : '10.99.201.209', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.5', 'dport' : 50005, 'daddr' : '10.99.201.213', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.5', 'dport' : 50005, 'daddr' : '10.99.201.197', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.1', 'dport' : 50001, 'daddr' : '10.99.201.129', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.81', 'dport' : 50001, 'daddr' : '10.99.201.197', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.17', 'dport' : 50001, 'daddr' : '10.99.201.213', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'family' : socket.AF_INET, 'sport' : 1, 'saddr' : '10.99.101.21', 'dport' : 50001, 'daddr' : '10.99.201.133', 'iface' : 'dummy0'}, ] _dispatcher_answers = [ 'tp_sockaddr', 'non_tp_sockaddr', 'tp_sockaddr_ports', 'non_tp_sockaddr_ports', 'tp_sockaddr_udp', 'non_tp_sockaddr_udp', 'tp_sockaddr_ports_udp', 'non_tp_sockaddr_ports_udp', 'tp_iface', 'non_tp_iface', 'tp_iface_ports', 'non_tp_iface_ports', 'tp_iface_udp', 'non_tp_iface_udp', 'tp_iface_ports_udp', 'non_tp_iface_ports_udp', 'tp_ifgroup', 'non_tp_ifgroup', 'tp_ifgroup_ports', 'non_tp_ifgroup_ports', 'tp_ifgroup_udp', 'non_tp_ifgroup_udp', 'tp_ifgroup_ports_udp', 'non_tp_ifgroup_ports_udp' ] _css_answers = ['AAA_ZAZ', 'AY_ZYA', 'ABZ_ZBA', 'AA_ZBZ', 'AYZ_ZB', 'AB_ZZZ', 'AZ_ZYA', 'AAZ_ZYZ', 'AA_ZBZ' ] _zone_answers = [ { 'client' : 'AAA', 'server' : 'ZAA'}, { 'client' : 'AYA', 'server' : 'ZBZ'}, { 'client' : 'AZA', 'server' : 'ZYZ'}, { 'client' : 'AZZ', 'server' : 'ZAZ'} ] for query in _queries: family = query['family'] message_query = create_query_msg(query['proto'], \ family, \ socket.inet_pton(family, query['saddr']), \ query['sport'], \ socket.inet_pton(family, query['daddr']), \ query['dport'], \ query['iface']) self.send_message(KZNL_MSG_QUERY, message_query, message_handler = self._query_message_handler) for i in range(len(_dispatcher_answers)): self.assertEqual(self.get_dispatcher_name(self._dumped_diszpancsers[i]), _dispatcher_answers[i]) for i in range(len(_css_answers)): self.assertEqual(self.get_service_name(self._dumped_diszpancsers[i + len(_dispatcher_answers)]), _css_answers[i]) for i in range(len(_zone_answers)): self.assertEqual(self.get_client_zone_name(self._dumped_diszpancsers[i + len(_dispatcher_answers) + len(_css_answers)]), _zone_answers[i]['client']) self.assertEqual(self.get_server_zone_name(self._dumped_diszpancsers[i + len(_dispatcher_answers) + len(_css_answers)]), _zone_answers[i]['server']) class KZorpTestCaseQueryNDim(KZorpBaseTestCaseQuery): def __init__(self, *args): KZorpBaseTestCaseQuery.__init__(self, *args) def tearDown(self): self.flush_all() def _run_query2(self, queries): for query in queries: family = query['family'] message_query = create_query_msg(query['proto'], \ query['family'], \ socket.inet_pton(family, query['saddr']), \ query['sport'], \ socket.inet_pton(family, query['daddr']), \ query['dport'], \ query['iface']) self.send_message(KZNL_MSG_QUERY, message_query, message_handler = \ lambda msg: self.assertEqual(self.get_service_name(msg), query['service'], str(query) + ' != ' + str(self.get_service_name(msg)))) def _run_query(self, _queries, _answers): for query in _queries: family = query['family'] message_query = create_query_msg(query['proto'], \ query['family'], \ socket.inet_pton(family, query['saddr']), \ query['sport'], \ socket.inet_pton(family, query['daddr']), \ query['dport'], \ query['iface']) self.send_message(KZNL_MSG_QUERY, message_query, message_handler = self._query_message_handler) for i in range(len(_answers)): self.assertEqual(self.get_service_name(self._dumped_diszpancsers[i]), _answers[i]) pass def test_n_dim_dispatcher_query(self): _dispatchers = [ { 'name' : 'n_dimension_with_ALL_rules', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 1, KZA_N_DIMENSION_PROTO : 1, KZA_N_DIMENSION_SRC_PORT : 2, KZA_N_DIMENSION_DST_PORT : 1, KZA_N_DIMENSION_SRC_IP : 2, KZA_N_DIMENSION_SRC_ZONE : 3, KZA_N_DIMENSION_DST_IP : 2, KZA_N_DIMENSION_DST_ZONE : 1, KZA_N_DIMENSION_IFGROUP : 1}, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['dummy0'], KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP], KZA_N_DIMENSION_SRC_PORT : [(2,3), (4,5)], KZA_N_DIMENSION_DST_PORT : [(5,6)], KZA_N_DIMENSION_SRC_IP : [InetDomain('10.99.201.5'), InetDomain('2.3.4.5/24')], KZA_N_DIMENSION_SRC_ZONE : ['AAZ', 'ZZ', 'Z'], KZA_N_DIMENSION_DST_IP : [InetDomain('10.99.101.149/16'), InetDomain('4.5.6.7/8')], KZA_N_DIMENSION_DST_ZONE : 'ZZZ', KZA_N_DIMENSION_IFGROUP : [1]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 2, KZA_N_DIMENSION_DST_IP : 3, KZA_N_DIMENSION_SRC_ZONE : 1, KZA_N_DIMENSION_SRC_IP : 2, KZA_N_DIMENSION_DST_PORT : 2, KZA_N_DIMENSION_SRC_PORT : 2, KZA_N_DIMENSION_PROTO : 1, KZA_N_DIMENSION_IFACE : 3 }, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['AZA', 'ZAZ'], KZA_N_DIMENSION_DST_IP : [InetDomain('8.7.6.5'), InetDomain('7.6.5.4/31'), InetDomain('9.8.7.6/25')], KZA_N_DIMENSION_SRC_ZONE : 'ZZ', KZA_N_DIMENSION_SRC_IP : [InetDomain('5.4.3.2/32'), InetDomain('6.5.4.3/30')], KZA_N_DIMENSION_DST_PORT : [(66,66),(100,200)], KZA_N_DIMENSION_SRC_PORT : [(23,24), (30, 40)], KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP], KZA_N_DIMENSION_IFACE : ['dummy0', 'dummy1', 'dummy2'] } } ] } ] _services = ['Z_Z'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.101.149', 'iface' : 'dummy0'}, ] _answers = ['Z_Z'] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_backtrack_iface_query(self): _dispatchers = [ { 'name' : 'n_dimension_backtrack', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 1, KZA_N_DIMENSION_DST_IP : 1, KZA_N_DIMENSION_IFGROUP : 1}, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['dummy0'], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.4/32')], KZA_N_DIMENSION_IFGROUP : [1]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 1, KZA_N_DIMENSION_DST_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['dummy3'], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.5')] } } ] } ] _services = ['A_A', 'Z_Z'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy3'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.5', 'iface' : 'dummy3'}, ] _answers = ['A_A', 'A_A', 'Z_Z'] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_backtrack_src_port_query(self): _dispatchers = [ { 'name' : 'n_dimension_backtrack', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 2, KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [(10,10),(15,20)], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.4/32')]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 1, KZA_N_DIMENSION_DST_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [(15,20)], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.5')] } } ] } ] _services = ['A_A', 'Z_Z'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 10, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 15, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 15, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.5', 'iface' : 'dummy0'}, ] _answers = ['A_A', 'A_A', 'Z_Z'] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_backtrack_dst_port_query(self): _dispatchers = [ { 'name' : 'n_dimension_backtrack', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 2, KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(10,10),(15,20)], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.4/32')]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 1, KZA_N_DIMENSION_DST_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(15,20)], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.5')] } } ] } ] _services = ['A_A', 'Z_Z'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 1, 'saddr' : '1.1.1.1', 'dport' : 10, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 1, 'saddr' : '1.1.1.1', 'dport' : 15, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 1, 'saddr' : '1.1.1.1', 'dport' : 15, 'family' : socket.AF_INET, 'daddr' : '1.2.3.5', 'iface' : 'dummy0'}, ] _answers = ['A_A', 'A_A', 'Z_Z'] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_backtrack_src_ip_query(self): _dispatchers = [ { 'name' : 'n_dimension_backtrack', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 2, KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.1.1.1/24'), InetDomain('1.1.1.1/32')], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.4/32')]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1, KZA_N_DIMENSION_DST_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.1.1.1/24')], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.5')] } } ] } ] _services = ['A_A', 'Z_Z'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '1.1.1.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '1.1.1.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.5', 'iface' : 'dummy0'}, ] _answers = ['A_A', 'A_A', 'Z_Z'] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_backtrack_src_zone_query(self): _dispatchers = [ { 'name' : 'n_dimension_backtrack', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 2, KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['ABA', 'AB'], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.4/32')]}, }, { 'rule_id' : 2, 'service' : 'Z_Z', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1, KZA_N_DIMENSION_DST_IP : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['AB'], KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.5')] } } ] } ] _services = ['A_A', 'Z_Z'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.65', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.69', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 2, 'saddr' : '10.99.201.69', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.5', 'iface' : 'dummy0'}, ] _answers = ['A_A', 'A_A', 'Z_Z'] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_iface_ifgroup_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2}, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['dummy0', 'dummy1'] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_IFGROUP : 1}, 'entry_values' : { KZA_N_DIMENSION_IFGROUP : [1] } }, ] }] _services = ['A_A', 'AA_AA'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy0'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy2'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy3'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy4'}, ] _answers = [ 'A_A', 'A_A', None, 'AA_AA', 'AA_AA', ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_iface_ifgroup_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 3, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 2}, 'entry_values' : { KZA_N_DIMENSION_IFACE : ['dummy0', 'dummy1'] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_IFGROUP : 1}, 'entry_values' : { KZA_N_DIMENSION_IFGROUP : [1] } }, { 'rule_id' : 3, 'service' : 'AAA_AAA', 'entry_nums' : { KZA_N_DIMENSION_IFACE : 0}, 'entry_values' : { KZA_N_DIMENSION_IFACE : [] } }, ] }] _services = ['A_A', 'AA_AA', 'AAA_AAA'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy2'}, ] _answers = [ 'AAA_AAA', ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_proto_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [ { 'rule_id' : 2, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_PROTO : 1}, 'entry_values' : { KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP] } }, ] }] _services = ['A_A'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', None ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_proto_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [ { 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_PROTO : 1}, 'entry_values' : { KZA_N_DIMENSION_PROTO : [socket.IPPROTO_TCP] } }, {'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_PROTO : 0}, 'entry_values' : { KZA_N_DIMENSION_PROTO : [] } }, ] }] _services = ['A_A', 'AA_AA'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'AA_AA' ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_src_port_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [{ 'rule_id' : 3, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 2}, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [(10,10), (60000, 65535)] } }, ] }] _services = ['A_A'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 10, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 60000, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 63000, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 65535, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 59999, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 9, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 11, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'A_A', 'A_A', 'A_A', None, None, None ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_src_port_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 2}, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [(10,10), (60000, 65535)] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_SRC_PORT : 0}, 'entry_values' : { KZA_N_DIMENSION_SRC_PORT : [] } }, ] }] _services = ['A_A', 'AA_AA'] packet = dict(proto=socket.IPPROTO_UDP, saddr='1.1.1.1', family=socket.AF_INET, daddr='1.2.3.4', iface='dummy1') queries = [ update_dict(packet, sport=10, dport=10, service='A_A'), update_dict(packet, sport=60000, dport=60000, service='A_A'), update_dict(packet, sport=63000, dport=63000, service='A_A'), update_dict(packet, sport=65535, dport=65535, service='A_A'), update_dict(packet, sport=59999, dport=59999, service='AA_AA'), update_dict(packet, sport=9, dport=9, service='AA_AA'), update_dict(packet, sport=11, dport=11, service='AA_AA'), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(queries) def test_n_dim_dst_port_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 1, 'rules' : [{ 'rule_id' : 3, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 2}, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(10,10), (60000, 65535)] } }, ] }] _services = ['A_A'] packet = dict(proto=socket.IPPROTO_UDP, sport=5, saddr='1.1.1.1', family=socket.AF_INET, daddr='1.2.3.4', iface='dummy1') queries = [ update_dict(packet, dport=10, service='A_A'), update_dict(packet, dport=60000, service='A_A'), update_dict(packet, dport=63000, service='A_A'), update_dict(packet, dport=65535, service='A_A'), update_dict(packet, dport=59999, service=None), update_dict(packet, dport=9, service=None), update_dict(packet, dport=11, service=None), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(queries) def test_n_dim_dst_port_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 2}, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [(10,10), (60000, 65535)] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_DST_PORT : 0}, 'entry_values' : { KZA_N_DIMENSION_DST_PORT : [] } }, ] }] _services = ['A_A', 'AA_AA'] packet = dict(proto=socket.IPPROTO_UDP, saddr='1.1.1.1', family=socket.AF_INET, daddr='1.2.3.4', iface='dummy1') queries = [ update_dict(packet, sport=10, dport=10, service='A_A'), update_dict(packet, sport=60000, dport=60000, service='A_A'), update_dict(packet, sport=63000, dport=63000, service='A_A'), update_dict(packet, sport=65535, dport=65535, service='A_A'), update_dict(packet, sport=59999, dport=59999, service='AA_AA'), update_dict(packet, sport=9, dport=9, service='AA_AA'), update_dict(packet, sport=11, dport=11, service='AA_AA'), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(queries) def test_n_dim_src_ip_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 7, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.0/24')] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.0/30')] } }, { 'rule_id' : 3, 'service' : 'AAA_AAA', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.0/31')] } }, { 'rule_id' : 4, 'service' : 'B_B', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.200')] } }, { 'rule_id' : 5, 'service' : 'C', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1, KZA_N_DIMENSION_SRC_IP6 : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetSubnet('2.0.0.0/8')], KZA_N_DIMENSION_SRC_IP6 : [Inet6Subnet('ffc0::1/127')] } }, { 'rule_id' : 6, 'service' : 'D', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1, KZA_N_DIMENSION_SRC_IP6 : 2 }, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetSubnet('2.3.4.5/32')], KZA_N_DIMENSION_SRC_IP6 : [Inet6Subnet('ffc0::0/10'), Inet6Subnet('ffc0::3/128')] } }, { 'rule_id' : 7, 'service' : 'E', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP6 : 1 }, 'entry_values' : { KZA_N_DIMENSION_SRC_IP6 : [Inet6Subnet('ffc0::2/127')] } }, ] }] _services = ['A_A', 'AA_AA', 'AAA_AAA', 'B_B', 'C', 'D', 'E'] ipv4_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET, daddr='1.1.1.1') ipv6_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET6, daddr='::') _queries = [ update_dict(ipv4_packet, saddr='1.2.3.4', service='A_A'), update_dict(ipv4_packet, saddr='1.2.3.2', service='AA_AA'), update_dict(ipv4_packet, saddr='1.2.3.1', service='AAA_AAA'), update_dict(ipv4_packet, saddr='1.2.3.200', service='B_B'), update_dict(ipv4_packet, saddr='1.2.2.5', service=None), update_dict(ipv6_packet, saddr='1234::', service=None), update_dict(ipv6_packet, saddr='ffc0::1', service="C"), update_dict(ipv4_packet, saddr='2.3.4.5', service="D"), update_dict(ipv4_packet, saddr='2.3.4.6', service="C"), update_dict(ipv6_packet, saddr='ffc0::2', service="E"), update_dict(ipv6_packet, saddr='ffc0::3', service="D"), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(_queries) def test_n_dim_src_ip_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 5, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.0/24')] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.0/30')] } }, { 'rule_id' : 3, 'service' : 'AAA_AAA', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.0/31')] } }, { 'rule_id' : 4, 'service' : 'B_B', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [InetDomain('1.2.3.200')] } }, { 'rule_id' : 5, 'service' : 'BB_BB', 'entry_nums' : { KZA_N_DIMENSION_SRC_IP : 0}, 'entry_values' : { KZA_N_DIMENSION_SRC_IP : [] } }, ] }] _services = ['A_A', 'AA_AA', 'AAA_AAA', 'B_B', 'BB_BB'] _queries = [ { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.2.3.4', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.2.3.2', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.2.3.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.2.3.200', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_TCP, 'sport' : 5, 'saddr' : '1.2.2.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'AA_AA', 'AAA_AAA', 'B_B', 'BB_BB' ] ipv4_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET, daddr='1.1.1.1') ipv6_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET6, daddr='::') _queries = [ update_dict(ipv4_packet, saddr='1.2.3.4', service='A_A'), update_dict(ipv4_packet, saddr='1.2.3.2', service='AA_AA'), update_dict(ipv4_packet, saddr='1.2.3.1', service='AAA_AAA'), update_dict(ipv4_packet, saddr='1.2.3.200', service='B_B'), update_dict(ipv4_packet, saddr='1.2.2.5', service='BB_BB'), update_dict(ipv6_packet, saddr='1234::', service='BB_BB'), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(_queries) def test_n_dim_src_zone_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 5, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['ABA'] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['AB'] } }, { 'rule_id' : 3, 'service' : 'AAA_AAA', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['A'] } }, { 'rule_id' : 4, 'service' : 'B_B', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['AAZ'] } }, { 'rule_id' : 5, 'service' : 'BB_BB', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['AY'] } }, ] }] _services = ['A_A', 'AA_AA', 'AAA_AAA', 'B_B', 'BB_BB'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.65', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.85', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.21', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.69', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'B_B', 'BB_BB', 'AAA_AAA', 'AA_AA', None ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_src_zone_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : ['ABA'] } }, { 'rule_id' : 5, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_SRC_ZONE : 0}, 'entry_values' : { KZA_N_DIMENSION_SRC_ZONE : [] } }, ] }] _services = ['A_A', 'AA_AA'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.65', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.5', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.85', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.21', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '10.99.201.69', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.2.3.4', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'AA_AA', 'AA_AA', 'AA_AA', 'AA_AA', 'AA_AA' ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_dst_ip_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 7, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.0/24')] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.0/30')] } }, { 'rule_id' : 3, 'service' : 'AAA_AAA', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.0/31')] } }, { 'rule_id' : 4, 'service' : 'B_B', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetDomain('1.2.3.200')] } }, { 'rule_id' : 5, 'service' : 'C', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1, KZA_N_DIMENSION_DST_IP6 : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetSubnet('2.0.0.0/8')], KZA_N_DIMENSION_DST_IP6 : [Inet6Subnet('ffc0::1/127')] } }, { 'rule_id' : 6, 'service' : 'D', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1, KZA_N_DIMENSION_DST_IP6 : 2 }, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetSubnet('2.3.4.5/32')], KZA_N_DIMENSION_DST_IP6 : [Inet6Subnet('ffc0::0/10'), Inet6Subnet('ffc0::3/128')] } }, { 'rule_id' : 7, 'service' : 'E', 'entry_nums' : { KZA_N_DIMENSION_DST_IP6 : 1 }, 'entry_values' : { KZA_N_DIMENSION_DST_IP6 : [Inet6Subnet('ffc0::2/127')] } }, ] }] _services = ['A_A', 'AA_AA', 'AAA_AAA', 'B_B', 'C', 'D', 'E'] ipv4_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET, saddr='1.1.1.1') ipv6_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET6, saddr='::') _queries = [ update_dict(ipv4_packet, daddr='1.2.3.4', service='A_A'), update_dict(ipv4_packet, daddr='1.2.3.2', service='AA_AA'), update_dict(ipv4_packet, daddr='1.2.3.1', service='AAA_AAA'), update_dict(ipv4_packet, daddr='1.2.3.200', service='B_B'), update_dict(ipv4_packet, daddr='1.2.2.5', service=None), update_dict(ipv6_packet, daddr='1234::', service=None), update_dict(ipv6_packet, daddr='ffc0::1', service="C"), update_dict(ipv4_packet, daddr='2.3.4.5', service="D"), update_dict(ipv4_packet, daddr='2.3.4.6', service="C"), update_dict(ipv6_packet, daddr='ffc0::2', service="E"), update_dict(ipv6_packet, daddr='ffc0::3', service="D"), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(_queries) def test_n_dim_dst_ip_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 1, 'service' : 'Non-empty', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 1, KZA_N_DIMENSION_DST_IP6 : 1, }, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [InetSubnet('1.2.3.0/24')], KZA_N_DIMENSION_DST_IP6 : [Inet6Subnet('1234::/128')], } }, { 'rule_id' : 5, 'service' : 'Empty', 'entry_nums' : { KZA_N_DIMENSION_DST_IP : 0}, 'entry_values' : { KZA_N_DIMENSION_DST_IP : [] } }, ] }] _services = ['Non-empty', 'Empty'] ipv4_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET, saddr='1.1.1.1') ipv6_packet = dict(proto=socket.IPPROTO_TCP, sport=5, dport=5, iface='dummy1', family=socket.AF_INET6, saddr='::') queries = [ update_dict(ipv4_packet, daddr='1.2.3.4', service='Non-empty'), update_dict(ipv4_packet, daddr='1.2.2.5', service='Empty'), update_dict(ipv6_packet, daddr='1234::', service='Non-empty'), update_dict(ipv6_packet, daddr='1235::', service='Empty'), ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query2(queries) def test_n_dim_dst_zone_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 5, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['ABA'] } }, { 'rule_id' : 2, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['AB'] } }, { 'rule_id' : 3, 'service' : 'AAA_AAA', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['A'] } }, { 'rule_id' : 4, 'service' : 'B_B', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['AAZ'] } }, { 'rule_id' : 5, 'service' : 'BB_BB', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['AY'] } }, ] }] _services = ['A_A', 'AA_AA', 'AAA_AAA', 'B_B', 'BB_BB'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.65', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.5', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.85', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.21', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.69', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'B_B', 'BB_BB', 'AAA_AAA', 'AA_AA', None ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) def test_n_dim_dst_zone_empty_query(self): _dispatchers = [{ 'name' : 'n_dimension_specific', 'type' : KZ_DPT_TYPE_N_DIMENSION, 'flags' : KZF_DPT_TRANSPARENT, 'proxy_port' : 1, 'num_rules' : 2, 'rules' : [{ 'rule_id' : 1, 'service' : 'A_A', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 1}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : ['ABA'] } }, { 'rule_id' : 5, 'service' : 'AA_AA', 'entry_nums' : { KZA_N_DIMENSION_DST_ZONE : 0}, 'entry_values' : { KZA_N_DIMENSION_DST_ZONE : [] } }, ] }] _services = ['A_A', 'AA_AA'] _queries = [ { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.65', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.5', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.85', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.21', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '10.99.201.69', 'iface' : 'dummy1'}, { 'proto' : socket.IPPROTO_UDP, 'sport' : 5, 'saddr' : '1.1.1.1', 'dport' : 5, 'family' : socket.AF_INET, 'daddr' : '1.1.1.1', 'iface' : 'dummy1'}, ] _answers = [ 'A_A', 'AA_AA', 'AA_AA', 'AA_AA', 'AA_AA', 'AA_AA' ] self.setup_service_dispatcher(_services, _dispatchers) self._run_query(_queries, _answers) class KZorpBaseTestCaseBind(KZorpComm): _bind_addrs = [ { 'instance' : KZ_INSTANCE_GLOBAL, 'family' : socket.AF_INET, 'addr' : socket.inet_pton(socket.AF_INET, '127.0.0.1'), 'port' : 50080, 'proto' : socket.IPPROTO_TCP }, { 'instance' : KZ_INSTANCE_GLOBAL, 'family' : socket.AF_INET, 'addr' : socket.inet_pton(socket.AF_INET, '127.0.0.2'), 'port' : 50080, 'proto' : socket.IPPROTO_TCP }, { 'instance' : KZ_INSTANCE_GLOBAL, 'family' : socket.AF_INET6, 'addr' : socket.inet_pton(socket.AF_INET6, 'fec0::1'), 'port' : 50080, 'proto' : socket.IPPROTO_TCP }, { 'instance' : KZ_INSTANCE_GLOBAL, 'family' : socket.AF_INET6, 'addr' : socket.inet_pton(socket.AF_INET6, 'fec0::2'), 'port' : 50080, 'proto' : socket.IPPROTO_TCP }, { 'instance' : KZ_INSTANCE_GLOBAL, 'family' : socket.AF_INET, 'addr' : socket.inet_pton(socket.AF_INET, '127.0.0.1'), 'port' : 50081, 'proto' : socket.IPPROTO_TCP }, { 'instance' : KZ_INSTANCE_GLOBAL, 'family' : socket.AF_INET, 'addr' : socket.inet_pton(socket.AF_INET, '127.0.0.1'), 'port' : 50080, 'proto' : socket.IPPROTO_UDP }, ] _dumped_bind_addrs = [] def __init__(self, *args): KZorpComm.__init__(self, *args) def setUp(self): self.start_transaction() for bind_addr in self._bind_addrs: msg_add_bind = NfnetlinkMessageAddBind(**bind_addr) self.send_message(KZNL_MSG_ADD_BIND, msg_add_bind) self.end_transaction() def tearDown(self): self.flush_all() class KZorpTestCaseBind(KZorpBaseTestCaseBind): _dumped_binds = [] def __init__(self, *args): KZorpBaseTestCaseBind.__init__(self, *args) def test_unicity_check_at_transaction(self): self.flush_all() self.start_transaction() for bind_addr in self._bind_addrs: msg_add_bind = NfnetlinkMessageAddBind(**bind_addr) self.send_message(KZNL_MSG_ADD_BIND, msg_add_bind) try: msg_add_bind = NfnetlinkMessageAddBind(**bind_addr) self.send_message(KZNL_MSG_ADD_BIND, msg_add_bind) except AssertionError as e: if e.args[0] != "talk with KZorp failed: result='-17' error='File exists'": raise e self.end_transaction() def test_unicity_check_at_instance(self): self.flush_all() self.start_transaction() for bind_addr in self._bind_addrs: msg_add_bind = NfnetlinkMessageAddBind(**bind_addr) self.send_message(KZNL_MSG_ADD_BIND, msg_add_bind) for bind_addr in self._bind_addrs: try: msg_add_bind = NfnetlinkMessageAddBind(**bind_addr) self.send_message(KZNL_MSG_ADD_BIND, msg_add_bind) except AssertionError as e: if e.args[0] != "talk with KZorp failed: result='-17' error='File exists'": raise e self.end_transaction() def _dump_bind_handler(self, message): self._dumped_binds.append(message) def get_bind(self): self.start_transaction() msg_get_bind = NfnetlinkMessageGetBind() self.send_message(KZNL_MSG_GET_BIND, msg_get_bind, message_handler = self._dump_bind_handler, dump = True) self.end_transaction() def test_flush(self): self.flush_all() self._dumped_binds = [] self.get_bind() self.assertEqual(len(self._dumped_binds), 0, "bind list not empty after flush; bind_num='%d'" % len(self._dumped_binds)) def test_add(self): self._dumped_binds = [] self.get_bind() self.assertEqual(len(self._dumped_binds), len(self._bind_addrs), "bind list not empty after flush; added_bind_num='%d' dumped_bind_num='%d'" % (len(self._dumped_binds), len(self._dumped_binds))) for i in range(len(self._bind_addrs)): msg_add_bind = NfnetlinkMessageAddBind(**self._bind_addrs[i]) self.assertEqual(msg_add_bind, self._dumped_binds[i].get_nfmessage()) def test_auto_flush(self): bind_addr_num = len(self._bind_addrs) orig_handle = self.handle self.handle = None self.create_handle() self._dumped_binds = [] self.get_bind() self.assertEqual(len(self._dumped_binds), len(self._bind_addrs)) for i in range(bind_addr_num): msg_add_bind = NfnetlinkMessageAddBind(**self._bind_addrs[i]) self.assertEqual(msg_add_bind, self._dumped_binds[i].get_nfmessage()) for bind_addr in self._bind_addrs: bind_addr['port'] = bind_addr['port'] + 10000 self.setUp() self._dumped_binds = [] self.get_bind() self.assertEqual(len(self._dumped_binds), len(self._bind_addrs) * 2) for i in range(bind_addr_num): msg_add_bind = NfnetlinkMessageAddBind(**self._bind_addrs[i]) self.assertEqual(msg_add_bind, self._dumped_binds[i].get_nfmessage()) for i in range(bind_addr_num): self._bind_addrs[i]['port'] = self._bind_addrs[i]['port'] - 10000 msg_add_bind = NfnetlinkMessageAddBind(**self._bind_addrs[i]) self.assertEqual(msg_add_bind, self._dumped_binds[i + bind_addr_num].get_nfmessage()) self.close_handle() self.handle = orig_handle self._dumped_binds = [] self.get_bind() self.assertEqual(len(self._dumped_binds), len(self._bind_addrs)) for i in range(bind_addr_num): msg_add_bind = NfnetlinkMessageAddBind(**self._bind_addrs[i]) self.assertEqual(msg_add_bind, self._dumped_binds[i].get_nfmessage()) self.reopen_handle() self._dumped_binds = [] self.get_bind() self.assertEqual(len(self._dumped_binds), 0) class KZorpTestResult(unittest.TextTestResult): def __init__(self, stream, descriptions, verbosity): super(KZorpTestResult, self).__init__(stream, descriptions, verbosity) def addFailure(self, test, err): super(KZorpTestResult, self).addFailure(test, err) def addError(self, test, err): super(KZorpTestResult, self).addError(test, err) class KZorpTestRunner(unittest.TextTestRunner): def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None): super(KZorpTestRunner, self).__init__(stream=stream, descriptions=descriptions, verbosity=verbosity, failfast=failfast, buffer=buffer, resultclass=KZorpTestResult) def _makeResult(self): return super(KZorpTestRunner, self)._makeResult() if __name__ == "__main__": if os.getenv("USER") != "root": print "ERROR: You need to be root to run the unit test" sys.exit(1) if glob.glob('/var/run/zorp/*.pid'): print "ERROR: pidfile(s) exist in /var/run/zorp directory. Zorp is running?" print " You should stop Zorp and/or delete pid files from /var/run/zorp" print " in order to run this test." sys.exit(1) unittest.main(testRunner=KZorpTestRunner) zorp-3.9.5/tests/kzorp/test_kzorp_sockopt.py000066400000000000000000000067211172670260400213640ustar00rootroot00000000000000#!/usr/bin/env python import os import sys import glob import socket socket.IP_TRANSPARENT = 19 socket.SO_KZORP_RESULT = 1678333 from kznf.kznfnetlink import * from kznf.nfnetlink import * class KZorpSockoptTest(object): def __init__(self, *args): super(KZorpSockoptTest, self).__init__(*args) self.handle = Handle() self.handle.register_subsystem(Subsystem(NFNL_SUBSYS_KZORP)) def exchange_message(self, message, payload): m = self.handle.create_message(NFNL_SUBSYS_KZORP, message, NLM_F_REQUEST | NLM_F_ACK) m.set_nfmessage(payload) result = self.handle.talk(m, (0, 0), None) if result: raise NfnetlinkException, "Error while talking to KZorp" def exchange_messages(self, message_list): for (message, payload) in message_list: self.exchange_message(message, payload) __setup_messages = \ ( (KZNL_MSG_START, create_start_msg("test", 123456789L)), (KZNL_MSG_FLUSH_SERVICE, create_flush_msg()), (KZNL_MSG_ADD_SERVICE, create_add_proxyservice_msg("service")), (KZNL_MSG_COMMIT, create_commit_msg()), (KZNL_MSG_START, create_start_msg(KZ_INSTANCE_GLOBAL, 123456789L)), (KZNL_MSG_FLUSH_ZONE, create_flush_msg()), (KZNL_MSG_ADD_ZONE, create_add_zone_msg("internet", 0, 0L, 0L, None, None)), (KZNL_MSG_ADD_ZONE_SVC_OUT, create_add_zone_svc_msg("internet", "*")), (KZNL_MSG_ADD_ZONE_SVC_IN, create_add_zone_svc_msg("internet", "*")), (KZNL_MSG_COMMIT, create_commit_msg()), (KZNL_MSG_START, create_start_msg("test", 123456789L)), (KZNL_MSG_FLUSH_DISPATCHER, create_flush_msg()), (KZNL_MSG_ADD_DISPATCHER, create_add_dispatcher_n_dimension("dispatcher", KZF_DPT_TRANSPARENT, 12345, 1)), (KZNL_MSG_ADD_RULE, create_add_n_dimension_rule_msg("dispatcher", 1, "service", {})), (KZNL_MSG_COMMIT, create_commit_msg()), ) def setUp(self): self.exchange_messages(self.__setup_messages) __teardown_messages = \ ( (KZNL_MSG_START, create_start_msg("test", 987654321L)), (KZNL_MSG_FLUSH_DISPATCHER, create_flush_msg()), (KZNL_MSG_COMMIT, create_commit_msg()), (KZNL_MSG_START, create_start_msg(KZ_INSTANCE_GLOBAL, 987654321L)), (KZNL_MSG_FLUSH_ZONE, create_flush_msg()), (KZNL_MSG_COMMIT, create_commit_msg()), (KZNL_MSG_START, create_start_msg("test", 987654321L)), (KZNL_MSG_FLUSH_SERVICE, create_flush_msg()), (KZNL_MSG_COMMIT, create_commit_msg()), ) def tearDown(self): self.exchange_messages(self.__teardown_messages) if __name__ == "__main__": if os.getenv("USER") != "root": print "ERROR: You need to be root to run the unit test" sys.exit(1) if glob.glob('/var/run/zorp/*.pid'): print "ERROR: pidfile(s) exist in /var/run/zorp directory. Zorp is running?" print " You should stop Zorp and/or delete pid files from /var/run/zorp" print " in order to run this test." sys.exit(1) test = KZorpSockoptTest() test.setUp() print "*" * 70 print "KZorp configuration set up, start get_kzorp_result, then connect to" print "any TCP port of the test host with netcat. get_kzorp_result should" print "then print the following following:\n" print "Cookie: 123456789, client zone: 'internet', server zone: 'internet'," print "dispatcher: 'dispatcher', service: 'service'\n" print "Then press Enter to flush the KZorp configuration" print "*" * 70 sys.stdin.readline() test.tearDown() zorp-3.9.5/tests/python/000077500000000000000000000000001172670260400152125ustar00rootroot00000000000000zorp-3.9.5/tests/python/Makefile.am000066400000000000000000000002171172670260400172460ustar00rootroot00000000000000EXTRA_DIST = test_inetdomain.py test_zone.py test_matcher.py test_dispatch.py test_nat.py noinst_SCRIPTS = runtest.sh TESTS = runalltests.sh zorp-3.9.5/tests/python/runtest.sh.in000066400000000000000000000001371172670260400176600ustar00rootroot00000000000000#!/bin/sh ZORP=../../zorp/zorp script=$1 shift $ZORP --no-syslog --no-caps -p $script exit $? zorp-3.9.5/tests/python/test_dispatch.py000066400000000000000000000121671172670260400204310ustar00rootroot00000000000000from Zorp.Core import * from Zorp.Plug import * from Zorp.Zorp import quit from traceback import * config.options.kzorp_enabled = FALSE def zorp(): try: Service('test', PlugProxy) # keyword argument is present that is processed by the C code Listener(SockAddrInet('0.0.0.0', 1999), 'test', transparent=TRUE) Listener(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), 'test', transparent=TRUE) Listener(DBIface('eth0', 1999), 'test', transparent=TRUE) Receiver(SockAddrInet('0.0.0.0', 1999), 'test', transparent=TRUE) Receiver(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), 'test', transparent=TRUE) Receiver(DBIface('eth0', 1999), 'test', transparent=TRUE) Dispatcher(DBSockAddr(SockAddrInet('0.0.0.0', 1999), protocol=ZD_PROTO_TCP), 'test', transparent=TRUE) Dispatcher(DBIface('eth0', 1999, protocol=ZD_PROTO_TCP), 'test', transparent=TRUE) Dispatcher(DBIfaceGroup(100, 1999, protocol=ZD_PROTO_TCP), 'test', transparent=TRUE) #Dispatcher(DBIfaceGroup('ifgroup', 1999, protocol=ZD_PROTO_TCP), 'test', transparent=TRUE) ZoneListener(SockAddrInet('0.0.0.0', 1999), {'all': 'test'}, transparent=TRUE) ZoneListener(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {'all': 'test'}, transparent=TRUE) ZoneListener(DBIface('eth0', 1999), {'all': 'test'}, transparent=TRUE) ZoneReceiver(SockAddrInet('0.0.0.0', 1999), {'all': 'test'}, transparent=TRUE) ZoneReceiver(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {'all': 'test'}, transparent=TRUE) ZoneReceiver(DBIface('eth0', 1999), {'all': 'test'}, transparent=TRUE) ZoneDispatcher(DBSockAddr(SockAddrInet('0.0.0.0', 1999), protocol=ZD_PROTO_TCP), {'all': 'test'}, transparent=TRUE) ZoneDispatcher(DBIface('eth0', 1999, protocol=ZD_PROTO_TCP), {'all': 'test'}, transparent=TRUE) ZoneDispatcher(DBIfaceGroup(100, 1999, protocol=ZD_PROTO_TCP), {'all': 'test'}, transparent=TRUE) #ZoneDispatcher(DBIfaceGroup('ifgroup', 1999, protocol=ZD_PROTO_TCP), {'all': 'test'}, transparent=TRUE) CSZoneListener(SockAddrInet('0.0.0.0', 1999), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneListener(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneListener(DBIface('eth0', 1999), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneReceiver(SockAddrInet('0.0.0.0', 1999), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneReceiver(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneReceiver(DBIface('eth0', 1999), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneDispatcher(DBSockAddr(SockAddrInet('0.0.0.0', 1999), protocol=ZD_PROTO_TCP), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneDispatcher(DBIface('eth0', 1999, protocol=ZD_PROTO_TCP), {('all', 'all'): 'test'}, transparent=TRUE) CSZoneDispatcher(DBIfaceGroup(100, 1999, protocol=ZD_PROTO_TCP), {('all', 'all'): 'test'}, transparent=TRUE) #CSZoneDispatcher(DBIfaceGroup('ifgroup', 1999, protocol=ZD_PROTO_TCP), {('all', 'all'): 'test'}, transparent=TRUE) # no keyword arguments Listener(SockAddrInet('0.0.0.0', 1999), 'test') Listener(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), 'test') Listener(DBIface('eth0', 1999), 'test') Receiver(SockAddrInet('0.0.0.0', 1999), 'test') Receiver(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), 'test') Receiver(DBIface('eth0', 1999), 'test') Dispatcher(DBSockAddr(SockAddrInet('0.0.0.0', 1999), protocol=ZD_PROTO_TCP), 'test') Dispatcher(DBIface('eth0', 1999, protocol=ZD_PROTO_TCP), 'test') ZoneListener(SockAddrInet('0.0.0.0', 1999), {'all': 'test'}) ZoneListener(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {'all': 'test'}) ZoneListener(DBIface('eth0', 1999), {'all': 'test'}) ZoneReceiver(SockAddrInet('0.0.0.0', 1999), {'all': 'test'}) ZoneReceiver(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {'all': 'test'}) ZoneReceiver(DBIface('eth0', 1999), {'all': 'test'}) ZoneDispatcher(DBSockAddr(SockAddrInet('0.0.0.0', 1999), protocol=ZD_PROTO_TCP), {'all': 'test'}) ZoneDispatcher(DBIface('eth0', 1999, protocol=ZD_PROTO_TCP), {'all': 'test'}) CSZoneListener(SockAddrInet('0.0.0.0', 1999), {('all', 'all'): 'test'}) CSZoneListener(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {('all', 'all'): 'test'}) CSZoneListener(DBIface('eth0', 1999), {('all', 'all'): 'test'}) CSZoneReceiver(SockAddrInet('0.0.0.0', 1999), {('all', 'all'): 'test'}) CSZoneReceiver(DBSockAddr(SockAddrInet('0.0.0.0', 1999)), {('all', 'all'): 'test'}) CSZoneReceiver(DBIface('eth0', 1999), {('all', 'all'): 'test'}) CSZoneDispatcher(DBSockAddr(SockAddrInet('0.0.0.0', 1999), protocol=ZD_PROTO_TCP), {('all', 'all'): 'test'}) CSZoneDispatcher(DBIface('eth0', 1999, protocol=ZD_PROTO_TCP), {('all', 'all'): 'test'}) Rule(service='test') except Exception, e: print_exc() quit(1) return 1 quit(0) return 1 zorp-3.9.5/tests/python/test_inetdomain.py000066400000000000000000000013601172670260400207520ustar00rootroot00000000000000from Zorp.Core import * from socket import inet_ntoa, inet_aton from traceback import print_exc import struct config.options.kzorp_enabled = FALSE true = 1 false = 0 def test(str, res, expect): if res != expect: print str, 'failed,', res, 'should be: ', expect else: print str, 'ok,', res def init(names, virtual_name, is_master): try: subnet = InetSubnet("192.168.0.1/24") test("netaddr(): ", subnet.addr_str(), "192.168.0.0") test("broadcast(): ", subnet.broadcast(), struct.unpack("I", inet_aton("192.168.0.255"))[0]) test("netmask(): ", subnet.netmask_int(), struct.unpack("I", inet_aton("255.255.255.0"))[0]) except Exception, e: print 'exception: fail', e print_exc() Zorp.quit(1) return 0 Zorp.quit(0) return 1 zorp-3.9.5/tests/python/test_input_path.py000066400000000000000000000051601172670260400210000ustar00rootroot00000000000000from Zorp.Plug import PlugProxy from Zorp.Stream import Stream from Zorp.Core import * from traceback import * from time import time, sleep config.options.kzorp_enabled = FALSE try: import profile do_profile = 1 except: do_profile = 0 class DummyPlug(PlugProxy): count = 0 def __init__(self, session): self.session = session DummyPlug.count = DummyPlug.count + 1 setattr(self.session, self.name, self) session.setProxy(self.name) log(session.session_id, CORE_SESSION, 5, "Proxy starting; class='%s', proxy='%s'", (self.__class__.__name__, self.name)) super(DummyPlug, self).__init__(session) count = 300 def benchmark(): global listener, count for i in range(0, count): listener.accepted(stream=Stream(3, "noname"), client_address=SockAddrInet('192.168.1.6', 5555), client_local=SockAddrInet('192.168.1.5', 80), client_listen=SockAddrInet('0.0.0.0', 56789)) def zorp(): global listener try: InetZone("test1", "192.168.0.0/24", inbound_services=["s1"], outbound_services=["s2"]) InetZone("test2", "192.168.0.32/27") InetZone("test3", "192.168.0.0/26") InetZone("test4", "192.168.0.64/27") InetZone("test5", "192.168.0.96/27") InetZone("test6", "192.168.0.0/25") InetZone("test7", "192.168.0.0/16") InetZone("test8", "192.168.1.1/32", admin_parent="test1") InetZone("test9", "192.168.1.2/32", admin_parent="test8") InetZone("test10", "192.168.1.3/32", admin_parent="test9", umbrella=1) InetZone("test11", "192.168.1.4/32", admin_parent="test9") InetZone("test12", "192.168.1.5/32", inbound_services=['*']) InetZone("test13", "192.168.1.6/32", outbound_services=['*']) InetZone("internet", "0.0.0.0/0", inbound_services=["s2"], outbound_services=["s1"]) Service('test-service', DummyPlug, router=TransparentRouter()) listener = CSZoneListener(SockAddrInet('0.0.0.0', 56789), services={('test1', 'test2'): 'test-service', ('test1', 'test3'): 'test-service', ('*', '*'): 'test-service'}) start = time() if do_profile: profile.run("benchmark()", 'profile.out') else: benchmark() end = time() if (DummyPlug.count != count): raise Exception("Proxy startup count did not match: count is %d, should be %d " % (DummyPlug.count, count)) except Exception, e: print_exc() quit(1) return 1 sleep(2) print 'Connection rate: %f' % (1 / ((end-start)/count)) quit(0) return 1 zorp-3.9.5/tests/python/test_matcher.py000066400000000000000000000041341172670260400202500ustar00rootroot00000000000000from Zorp.Core import * from Zorp.Zorp import quit from traceback import * config.options.kzorp_enabled = FALSE class SubstringMatcher(AbstractMatcher): def __init__(self, pattern = ""): AbstractMatcher.__init__(self) self.pattern = pattern def checkMatch(self, str): return (str.find(self.pattern) != -1) def test(matcher_policy, str, should_accept): if matcher_policy.name: mname = matcher_policy.name else: mname = "(unnamed)" print "Testing str='", str, "', matcher='", mname, "', should_accept='", should_accept, "'" res = matcher_policy.matcher.checkMatch(str) if res == should_accept: print "Success" else: print "Failed" raise 'test error' def init(names, virtual_name, is_master): try: a = MatcherPolicy("a", SubstringMatcher(pattern="a")) b = MatcherPolicy("b", SubstringMatcher(pattern="b")) c = MatcherPolicy("c", SubstringMatcher(pattern="c")) a_or_b = MatcherPolicy("a_or_b", CombineMatcher(expr=[Z_OR, "a", "b"])) a_or_b_or_c = MatcherPolicy("a_or_b_or_c", CombineMatcher(expr=[Z_OR, "a", "b", "c"])) not_a_or_b_and_c = MatcherPolicy("not_a_or_b_and_c", CombineMatcher( expr=[Z_AND, c, CombineMatcher(expr=[Z_NOT, a_or_b])] )) stacked_matcher = MatcherPolicy("stacked", CombineMatcher((Z_AND, c, (Z_NOT, a_or_b)) )) test(a, "alma", TRUE) test(a, "korte", FALSE) test(a_or_b, "alma", TRUE) test(a_or_b, "birskorte", TRUE) test(a_or_b, "birsalma", TRUE) test(a_or_b, "korte", FALSE) test(not_a_or_b_and_c, "korte", FALSE) # c missing test(not_a_or_b_and_c, "cseresznye", TRUE) test(not_a_or_b_and_c, "almaecet", FALSE) # a or b is true test(not_a_or_b_and_c, "borecet", FALSE) # a or b is true test(stacked_matcher, "korte", FALSE) # c missing test(stacked_matcher, "cseresznye", TRUE) test(stacked_matcher, "almaecet", FALSE) # a or b is true test(stacked_matcher, "borecet", FALSE) # a or b is true test(a_or_b_or_c, "korte", FALSE) test(a_or_b_or_c, "cseresznye", TRUE) test(a_or_b_or_c, "almaecet", TRUE) test(a_or_b_or_c, "borecet", TRUE) except Exception, e: print_exc() quit(1) return 1 quit(0) return 1 zorp-3.9.5/tests/python/test_nat.py000066400000000000000000000027371172670260400174160ustar00rootroot00000000000000from Zorp.Core import * from Zorp.Plug import * from Zorp.Session import MasterSession from Zorp.NAT import NAT_SNAT, NAT_DNAT from Zorp.Zorp import quit from traceback import * config.options.kzorp_enabled = FALSE def testcase(nat, session, addrs, type, expected_result): res = nat.performTranslation(session, addrs, type) if not res.equal(expected_result): print 'invalid result res=%s, expected_result=%s' % (res.format(), expected_result.format()) raise ValueError def zorp(): try: s = MasterSession() s.setService(Service("s1", None)) nat = NATPolicy('test', GeneralNAT( [(InetSubnet('0.0.0.0/32'), InetSubnet('10.0.0.0/8'), InetSubnet('20.0.0.0/8')), (InetSubnet('0.0.0.0/32'), InetSubnet('11.0.0.0/8'), InetSubnet('192.168.0.0/24')), (Inet6Subnet('::/128'), Inet6Subnet('1200::/8'), Inet6Subnet('2300::/8')), ])) testcase(nat, s, (None, SockAddrInet('10.0.0.1', 8888)), NAT_DNAT, SockAddrInet('20.0.0.1', 8888)) testcase(nat, s, (None, SockAddrInet('11.0.0.0', 8888)), NAT_DNAT, SockAddrInet('192.168.0.0', 8888)) testcase(nat, s, (None, SockAddrInet('11.0.1.1', 8888)), NAT_DNAT, SockAddrInet('192.168.0.1', 8888)) testcase(nat, s, (None, SockAddrInet('11.255.255.255', 8888)), NAT_DNAT, SockAddrInet('192.168.0.255', 8888)) testcase(nat, s, (None, SockAddrInet6('1234::', 8888)), NAT_DNAT, SockAddrInet6('2334::', 8888)) except Exception, e: print_exc() quit(1) return 1 quit(0) return 1 zorp-3.9.5/tests/python/test_zone.py000066400000000000000000000103771172670260400176060ustar00rootroot00000000000000from Zorp.Core import * from Zorp.Zorp import quit from Zorp.Zone import Zone from Zorp.Session import MasterSession from traceback import * from time import time from socket import htonl config.options.kzorp_enabled = FALSE def test(str, res, expect): if res != expect: print str, 'failed,', res, 'should be: ', expect raise 'test error' else: print str, 'ok,', res def init(names, virtual_name, is_master): try: t1 = Zone("test1", "192.168.0.0/24", inbound_services=["s1"], outbound_services=["s2"]) t2 = Zone("test2", "192.168.0.32/27") t3 = Zone("test3", "192.168.0.0/26") t4 = Zone("test4", "192.168.0.64/27") t5 = Zone("test5", "192.168.0.96/27") t6 = Zone("test6", "192.168.0.0/25") t7 = Zone("test7", "192.168.0.0/16") t8 = Zone("test8", "192.168.1.1/32", admin_parent="test1") t9 = Zone("test9", "192.168.1.2/32", admin_parent="test8") t10 = Zone("test10", "192.168.1.3/32", admin_parent="test9", umbrella=1) t11 = Zone("test11", "192.168.1.4/32", admin_parent="test9") t12 = Zone("test12", "192.168.1.5/32", inbound_services=['*']) t13 = Zone("test13", "192.168.1.6/32", outbound_services=['*']) t14 = Zone("test14", "192.168.0.184", outbound_services=['*']) t15 = Zone("test15", "dead:beef:baad:c0ff:ee00:1122:3344:5566/127", outbound_services=['*']) test('192.168.0.1', Zone.lookup(SockAddrInet('192.168.0.1', 10)), t3) test('192.168.0.33', Zone.lookup(SockAddrInet('192.168.0.33', 10)), t2) test('192.168.0.65', Zone.lookup(SockAddrInet('192.168.0.65', 10)), t4) test('192.168.0.97', Zone.lookup(SockAddrInet('192.168.0.97', 10)), t5) test('192.168.0.129', Zone.lookup(SockAddrInet('192.168.0.129', 10)), t1) test('192.168.1.129', Zone.lookup(SockAddrInet('192.168.1.129', 10)), t7) test('192.168.0.184', Zone.lookup(SockAddrInet('192.168.0.184', 10)), t14) test('dead:beef:baad:c0ff:ee00:1122:3344:5566', Zone.lookup(SockAddrInet6('dead:beef:baad:c0ff:ee00:1122:3344:5566', 10)), t15) test('dead:beef:baad:c0ff:ee00:1122:3344:5566', Zone.lookup(SockAddrInet6('dead:beef:baad:c0ff:ee00:1122:3344:5567', 10)), t15) inet = Zone("internet", "0.0.0.0/0", inbound_services=["s2"], outbound_services=["s1"]) test('1.1.1.1', Zone.lookup(SockAddrInet('1.1.1.1', 10)), inet) s = MasterSession() s.setService(Service("s1", None)) s.setServer(SockAddrInet('192.168.1.2', 9999)) test('service s1#1', t1.isInboundServicePermitted(s.service), ZV_ACCEPT) test('service s1#2', t1.isOutboundServicePermitted(s.service), ZV_REJECT) test('service s1#3', inet.isInboundServicePermitted(s.service), ZV_REJECT) test('service s1#4', inet.isOutboundServicePermitted(s.service), ZV_ACCEPT) ### test('service s1#5', t10.isOutboundServicePermitted(s.service), ZV_REJECT) test('service s1#6', t10.isInboundServicePermitted(s.service), ZV_REJECT) test('service s1#7', t11.isOutboundServicePermitted(s.service), ZV_REJECT) test('service s1#8', t11.isInboundServicePermitted(s.service), ZV_ACCEPT) test('service s1#9', t12.isInboundServicePermitted(s.service), ZV_ACCEPT) test('service s1#10', t12.isOutboundServicePermitted(s.service), ZV_REJECT) test('service s1#11', t13.isOutboundServicePermitted(s.service), ZV_ACCEPT) test('service s1#12', t13.isInboundServicePermitted(s.service), ZV_REJECT) s.service = Service("s2", None) test('service s2#1', t1.isInboundServicePermitted(s.service), ZV_REJECT) test('service s2#2', t1.isOutboundServicePermitted(s.service), ZV_ACCEPT) test('service s2#3', inet.isInboundServicePermitted(s.service), ZV_ACCEPT) test('service s2#4', inet.isOutboundServicePermitted(s.service), ZV_REJECT) ### test('service s2#5', t10.isInboundServicePermitted(s.service), ZV_REJECT) test('service s2#6', t10.isOutboundServicePermitted(s.service), ZV_REJECT) test('service s2#7', t11.isOutboundServicePermitted(s.service), ZV_ACCEPT) test('service s2#8', t11.isInboundServicePermitted(s.service), ZV_REJECT) test('service s2#9', t12.isInboundServicePermitted(s.service), ZV_ACCEPT) test('service s2#10', t12.isOutboundServicePermitted(s.service), ZV_REJECT) test('service s2#11', t13.isOutboundServicePermitted(s.service), ZV_ACCEPT) test('service s2#12', t13.isInboundServicePermitted(s.service), ZV_REJECT) except Exception, e: print_exc() quit(1) return 1 quit(0) return 1 zorp-3.9.5/tests/tools/000077500000000000000000000000001172670260400150315ustar00rootroot00000000000000zorp-3.9.5/tests/tools/Agent/000077500000000000000000000000001172670260400160675ustar00rootroot00000000000000zorp-3.9.5/tests/tools/Agent/TestAgent.py000066400000000000000000000000271172670260400203360ustar00rootroot00000000000000class TestAgent: pass zorp-3.9.5/tests/tools/Agent/TestAgentImpl.py000066400000000000000000000200551172670260400211630ustar00rootroot00000000000000from TestAgent import TestAgent from cPickle import Pickler, Unpickler import socket, select, re import types import sys,os,time import exceptions import subprocess import shlex import signal import string import traceback LOGGING = 1 def search_file(filename, search_path): file_found = 0 paths = search_path.split(os.path.pathsep) for path in paths: if path and os.path.exists(os.path.join(path, filename)): return os.path.abspath(os.path.join(path, filename)) return None class TestAgentImpl(TestAgent): def __init__(self, input, output): self.input = input self.output = output self.exit_dispatch = 0 self.handles = {} self.child_processes = [] self.log_enabled = LOGGING if self.log_enabled: self.logfile = open('log','a') def __del__(self): self.input.close() self.output.close() self.__kill_children() if self.log_enabled: self.logfile.close() def log(self,msg): if self.log_enabled: self.logfile.write(msg+'\n') def supervise_start(self, command, env=None): args = shlex.split(command) if args[0][0] != '/': # not an absolute filename if args[0].find('/') >= 0: # relative path that has directory component, search it in the ZWA, /usr and / prefixes search_path = '/usr:/' if os.environ.has_key("ZWA_INSTALL_DIR"): search_path = os.environ["ZWA_INSTALL_DIR"] + ':' + search_path else: # only binary name, search it in the PATH augmented with ZWA directories search_path = os.environ["PATH"] search_path = '/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:' + search_path if env and env.has_key("PATH"): search_path = env["PATH"] + ':' + search_path if os.environ.has_key("ZWA_INSTALL_DIR"): search_path = os.environ["ZWA_INSTALL_DIR"] + '/bin:' + os.environ["ZWA_INSTALL_DIR"] + '/sbin:' + search_path args[0] = search_file(args[0], search_path) if not args[0]: raise Exception("Error locating command to run: %s" % command) sp = subprocess.Popen(args, env=env) self.child_processes.append(sp) time.sleep(3) return sp.pid def supervise_end(self, pid): ndx = 0 for child in self.child_processes: if child.pid == pid: # FIXME: on windows we need to use win32api.TerminateProcess as os.kill isn't available try: os.kill(pid, signal.SIGTERM) except OSError: pass time.sleep(3) retries = 0 while child.poll() == None and retries < 5: retries += 1 time.sleep(1) if child.poll() == None: try: os.kill(pid, signal.SIGKILL) except OSError: pass retries = 0 while child.poll() == None and retries < 5: retries += 1 time.sleep(1) if child.poll() != None: del self.child_processes[ndx] return child.poll() else: raise Exception("Error killing supervised process") ndx += 1 def __check_children(self): ndx = 0 for child in self.child_processes: if child.poll(): raise Exception("Supervised process exited prematurely, exit code: %d" % child.poll()) def __kill_children(self): ndx = 0 for child in self.child_processes: self.supervise_end(child.pid) def run(self, cmd): return os.system(cmd) def dispatch_command(self): cmd = Unpickler(self.input).load() self.log("command get: "+cmd[0]) if not hasattr(self, cmd[0]) or type(getattr(self, cmd[0])) != types.MethodType: Pickler(self.output, 1).dump(None) self.output.flush() f = getattr(self, cmd[0]) self.log("params: "+`cmd[1:]`) start = time.time() now = start try: self.__check_children() res = apply(f, cmd[1:]) except Exception, e: res = 'Error: %s' % e traceback.print_exc(traceback, sys.stderr) self.log("Exception "+res) #self.exit_dispatch = 1 self.log('waited: '+`'%.2f' % (now-start)`) self.log('result to send: '+`res`) Pickler(self.output, 1).dump(res) self.output.flush() if self.exit_dispatch: return 0 else: return 1 def socket(self, family, socktype): f = socket.socket(family, socktype) f.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) socket.IP_TRANSPARENT = 19 f.setsockopt(socket.SOL_IP, socket.IP_TRANSPARENT, 1) self.handles[f.fileno()] = f return f.fileno() def bind(self, fd, local): f = self.handles[fd] f.bind(local) def listen(self, fd, backlog): f = self.handles[fd] f.listen(backlog) def accept(self, fd, timeout=30): f = self.handles[fd] a,qqq ,qqqq = self.select([f],[],[],timeout) if a != []: res = f.accept() newfd = res[0].fileno() self.handles[newfd] = res[0] return (newfd, res[1]) else: raise Exception, 'no connection to accept' def connect(self, fd, remote): f = self.handles[fd] try: f.connect(remote) except: raise Exception, "can't connect" def select(self, r, w, x, timeout): return select.select(r, w, x, timeout) def read(self, fd, bufsize, timeout=5): f = self.handles[fd] self.log("in read: socket = "+`f`) a,qqq ,qqqq = self.select([f],[],[], timeout) if a != []: try: ret = f.recv(bufsize) except Exception, e: self.log("except. caught: " + `sys.exc_info()`) if e[0] == 32: ret = f.recv(bufsize) else: raise socket.error, e self.log('received:'+`ret`) return ret else: raise Exception, 'nothing to read' def readfrom(self, fd, bufsize, timeout=5): f = self.handles[fd] self.log("in read: socket = "+`f`) a,qqq ,qqqq = self.select([f],[],[], timeout) if a != []: ret = f.recvfrom(bufsize) self.log('received:'+`ret`) return ret else: raise Exception, 'nothing to read' def write(self, fd, chunk): f = self.handles[fd] return f.send(chunk) def writeto(self, fd, chunk, address): f = self.handles[fd] return f.sendto(chunk,address) def close(self, fd): self.log("closing conn. "+`fd`) f = self.handles[fd] f.close() del self.handles[fd] def iptables_save(self, family): if family == socket.AF_INET: cmd = "iptables-save" else: cmd = "ip6tables-save" p = subprocess.Popen(cmd, stdout=subprocess.PIPE) out = p.stdout.read() ret = '' while out: ret += out out = p.stdout.read() p.wait() return ret def iptables_restore(self, family, ruleset): if family == socket.AF_INET: cmd = "iptables-restore" else: cmd = "ip6tables-restore" p = subprocess.Popen(cmd, stdin=subprocess.PIPE) p.stdin.write(ruleset) p.stdin.close() p.wait() return p.returncode def quit(self): self.exit_dispatch = 1 return None zorp-3.9.5/tests/tools/Agent/TestAgentStub.py000066400000000000000000000066351172670260400212070ustar00rootroot00000000000000from TestAgent import TestAgent from cPickle import Pickler, Unpickler import types, traceback, sys, time, socket, subprocess class TestRunException(Exception): pass class RMIFile: def __init__(self, agent, fd): self.agent = agent self.fd = fd self.closed = 0 def __del__(self): try: self.close() except (IOError, EOFError, TestRunException): pass def fileno(self): return self.fd def bind(self, local): return self.agent.send_command('bind', self.fd, local) def connect(self, remote): return self.agent.send_command('connect', self.fd, remote) def listen(self, backlog): return self.agent.send_command('listen', self.fd, backlog) def accept(self, timeout=None): res = self.agent.send_command('accept', self.fd, timeout) r = RMIFile(self.agent, res[0]) return (r, res[1]) def read(self, maxbytes=-1): a = self.agent.send_command('read', self.fd, maxbytes) return a def readfrom(self, maxbytes=-1): a = self.agent.send_command('readfrom', self.fd, maxbytes) return a def write(self, chunk): return self.agent.send_command('write', self.fd, chunk) def writeto(self, chunk, address): a = self.agent.send_command('writeto',self.fd, chunk, address) return a def close(self): if not self.closed: self.agent.send_command('close', self.fd) self.closed = 1 def disconnected(self): return self.agent.send_command('disconnected', self.fd) class TestAgentStub(TestAgent): def __init__(self, cmd, role): self.role = role self.output = None self.input = None self.impl = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) self.output, self.input = self.impl.stdout, self.impl.stdin def send_command(self, *args): verb_string = 'to %s %s ' % (self.role, args,) # verbose('sendcommand args: '+`args`) Pickler(self.input, 1).dump(args) self.input.flush() res = Unpickler(self.output).load() verb_string = verb_string + '= %s' % (`res`,) #print(verb_string) >> sys.stderr if type(res) == types.StringType and res[:5] == 'Error': raise TestRunException(res) return res def iptables_save(self, family=socket.AF_INET): return self.send_command('iptables_save', family) def iptables_restore(self, family=socket.AF_INET, ruleset=""): return self.send_command('iptables_restore', family, ruleset) def socket(self, family, socktype): fd = self.send_command('socket', family, socktype) return RMIFile(self, fd) def select(self, r, w, x, timeout = None): r_no = map(lambda x: x.fileno(), r) w_no = map(lambda x: x.fileno(), w) x_no = map(lambda x: x.fileno(), x) (r_res, w_res, x_res) = self.send_command('select', r_no, w_no, x_no, timeout) r_obj = [] for xx in r: if xx.fileno() in r_res: r_obj.append(xx) w_obj = [] for xx in w: if xx.fileno() in w_res: w_obj.append(xx) x_obj = [] for xx in x: if xx.fileno() in x_res: x_obj.append(xx) return (r_obj, w_obj, x_obj) def supervise_start(self, cmd): return self.send_command('supervise_start', cmd) def supervise_end(self, pid): return self.send_command('supervise_end', pid) def quit(self): self.send_command('quit') zorp-3.9.5/tests/tools/Agent/__init__.py000066400000000000000000000000001172670260400201660ustar00rootroot00000000000000zorp-3.9.5/tests/tools/Agent/test-agent.py000066400000000000000000000004541172670260400205170ustar00rootroot00000000000000#!/usr/bin/env python from TestAgentImpl import TestAgentImpl import sys,os try: os.system("rm -f log") i = TestAgentImpl(sys.stdin, sys.stdout) while i.dispatch_command(): i.log("dispatch new command") pass i.log("get out of msg loop") except (EOFError, IOError, KeyboardInterrupt): pass zorp-3.9.5/tests/tools/Makefile.am000066400000000000000000000003431172670260400170650ustar00rootroot00000000000000pkglibdir=$(libdir)/zorp/tests pkglib_PROGRAMS = tos_receive udp_accept udp6_accept tos_receive_SOURCES = tos_receive.c udp_accept_SOURCES = udp_accept.c udp6_accept_SOURCES = udp6_accept.c EXTRA_DIST = tproxy-test.py Agent zorp-3.9.5/tests/tools/tos_receive.c000066400000000000000000000050521172670260400175060ustar00rootroot00000000000000#include #include #include #include #include #include #include #define PORT 12345 int make_socket (uint16_t port) { int sock, flag = 1; struct sockaddr_in name; /* Create the socket. */ sock = socket(PF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } /* Set the reuse flag. */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) { perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); exit(EXIT_FAILURE); } /* Give the socket a name. */ name.sin_family = AF_INET; name.sin_port = htons(port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock, (struct sockaddr *) &name, sizeof (name)) < 0) { perror("bind"); exit(EXIT_FAILURE); } return sock; } void print_tos(int sock) { unsigned char buf[256]; socklen_t size; size = sizeof(buf); if (getsockopt(sock, SOL_IP, IP_PKTOPTIONS, &buf, &size) < 0) { perror("getsockopt(SOL_IP, IP_PKTOPTIONS)"); exit(EXIT_FAILURE); } else { struct msghdr msg; struct cmsghdr *cmsg; int tos_found = 0; msg.msg_controllen = size; msg.msg_control = buf; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TOS) { unsigned char tos = *((unsigned char *) CMSG_DATA(cmsg)); tos_found = 1; fprintf(stderr, "TOS: 0x%x\n", tos); } } if (!tos_found) { fprintf(stderr, "Unable to query TOS\n"); exit(EXIT_FAILURE); } } } int main(void) { int sock, new; int flag; struct sockaddr_in clientname; unsigned char buf[256]; socklen_t size; sock = make_socket(PORT); if (listen(sock, 1) < 0) { perror("listen"); exit(EXIT_FAILURE); } flag = 1; if (setsockopt(sock, SOL_IP, IP_RECVTOS, &flag, sizeof(flag)) < 0) { perror("setsockopt(SOL_IP, IP_RECVTOS)"); exit(EXIT_FAILURE); } fprintf(stderr, "Listening on port %d\n", PORT); size = sizeof(clientname); new = accept(sock, (struct sockaddr *) &clientname, &size); if (new < 0) { perror("accept"); exit(EXIT_FAILURE); } fprintf(stderr, "Connect from %s:%hu\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port)); print_tos(new); if (read(new, buf, 1) < 0) { perror("read"); exit(EXIT_FAILURE); } print_tos(new); close(new); close(sock); exit(EXIT_SUCCESS); } zorp-3.9.5/tests/tools/tproxy-test.py000066400000000000000000000311201172670260400177220ustar00rootroot00000000000000#!/usr/bin/python import os, sys, socket, traceback, time, getopt, subprocess # global params zero_ip4 = '0.0.0.0' zero_ip6 = '::' #Host used as a proxy (needs passwordless root ssh access) proxy_ip4 = '10.30.8.130' proxy_ip6 = 'dead:1::2' proxy_port = 50080 #Nonexistent addres, we just need a host route on the client through the proxy for this address target_ip4 = '5.6.7.8' target_ip6 = 'dead:2::1' target_port = 80 def connect_agent(): from Agent import TestAgentStub ssh_options="-o UserKnownHostsFile=/dev/null -o NumberOfPasswordPrompts=0 -o StrictHostKeyChecking=no -o LogLevel=ERROR" if os.system("rsync -e 'ssh %s' -a Agent root@%s:" % (ssh_options, proxy_ip4)) != 0: raise Exception, "Error syncing agent" return TestAgentStub.TestAgentStub("ssh %s root@%s Agent/test-agent.py" % (ssh_options, proxy_ip4,), 'tproxy') def testcases(): # IPv4 tproxy_rule_only_ipv4_sockets = ( # sockets that are irrelevant to our redirection, their existance should not # cause any connections to be established ( # irrelevant, because of port number (zero_ip4, 5678, True), (proxy_ip4, 5678), # redirect should override even those listeners that are bound explicitly (zero_ip4, 80, True), (proxy_ip4, 80), (target_ip4, 80), (target_ip4, 50080, True) ), # sockets that should match, in reverse-preference order, e.g. the # connection should always establish to the last one ((zero_ip4, 50080, True), (proxy_ip4, 50080)) ) tproxy_plus_socket_rules_ipv4_sockets = ( # sockets that are irrelevant to our redirection, their existance should not # cause any connections to be established ( # irrelevant, because of port number (zero_ip4, 5678, True), (proxy_ip4, 5678), # redirect should override even those listeners that are bound explicitly (zero_ip4, 80, True), (proxy_ip4, 80), (target_ip4, 50080, True) ), # sockets that should match, in reverse-preference order, e.g. the # connection should always establish to the last one ( # because of the socket match, we get a connection on the target address # this is when the proxy opens a dynamic listener (target_ip4, 80, True), (zero_ip4, 50080, True), (proxy_ip4, 50080) ) ) # ipv6 tproxy_rule_only_ipv6_sockets = ( # sockets that are irrelevant to our redirection, their existance should not # cause any connections to be established ( # irrelevant, because of port number (zero_ip6, 5678, True), (proxy_ip6, 5678), # redirect should override even those listeners that are bound explicitly (zero_ip6, 80, True), (proxy_ip6, 80), (target_ip6, 80), (target_ip6, 50080, True) ), # sockets that should match, in reverse-preference order, e.g. the # connection should always establish to the last one ((zero_ip6, 50080, True), (proxy_ip6, 50080)) ) tproxy_plus_socket_rules_ipv6_sockets = ( # sockets that are irrelevant to our redirection, their existance should not # cause any connections to be established ( # irrelevant, because of port number (zero_ip6, 5678, True), (proxy_ip6, 5678), # redirect should override even those listeners that are bound explicitly (zero_ip6, 80, True), (proxy_ip6, 80), (target_ip6, 50080, True) ), # sockets that should match, in reverse-preference order, e.g. the # connection should always establish to the last one ( # because of the socket match, we get a connection on the target address # this is when the proxy opens a dynamic listener (target_ip6, 80, True), (zero_ip6, 50080, True), (proxy_ip6, 50080) ) ) tproxy_sockets = { (socket.AF_INET, False): tproxy_rule_only_ipv4_sockets, (socket.AF_INET, True): tproxy_plus_socket_rules_ipv4_sockets, (socket.AF_INET6, False): tproxy_rule_only_ipv6_sockets, (socket.AF_INET6, True): tproxy_plus_socket_rules_ipv6_sockets, } return tproxy_sockets def load_iptables(a, family=socket.AF_INET, socket_type=socket.SOCK_STREAM, socket_rule=False, explicit_on_ip=False): header = """ *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -p icmpv6 -j ACCEPT -A INPUT -p tcp --dport 22 -j ACCEPT -A INPUT -m mark --mark 0x80000000/0x80000000 -j ACCEPT -A INPUT -j LOG --log-prefix "PF/INPUT: DROP " -A INPUT -j DROP -A FORWARD -j LOG --log-prefix "PF/FORWARD: DROP " -A FORWARD -j DROP COMMIT *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] :DIVERT - [0:0] """ subst = {} if socket_type == socket.SOCK_STREAM: subst['proto'] = 'tcp' else: subst['proto'] = 'udp' subst['target_port'] = target_port subst['proxy_port'] = proxy_port if family == socket.AF_INET: subst['proxy_ip'] = proxy_ip4 else: subst['proxy_ip'] = proxy_ip6 rules = header if socket_rule: rules += """-A PREROUTING -m socket --transparent -j DIVERT\n""" if explicit_on_ip: rules += """ -A PREROUTING -p %(proto)s -m %(proto)s --dport %(target_port)d -j TPROXY --on-port %(proxy_port)d --on-ip %(proxy_ip)s --tproxy-mark 0x80000000/0x80000000""" % subst else: rules += """ -A PREROUTING -p %(proto)s -m %(proto)s --dport %(target_port)d -j TPROXY --on-port %(proxy_port)d --tproxy-mark 0x80000000/0x80000000""" % subst rules += """ -A DIVERT -j MARK --set-xmark 0x80000000/0x80000000 -A DIVERT -j ACCEPT COMMIT """ % subst print rules a.iptables_restore(family, rules) def open_listener(a, family, socket_type, addr): s = a.socket(family, socket_type) s.bind(addr[0:2]) if socket_type == socket.SOCK_STREAM: s.listen(255) return s def run_sockets(a, family=socket.AF_INET, socket_type=socket.SOCK_STREAM, socket_rule=False, explicit_on_ip=False, sockets=()): skip_irrelevant = False load_iptables(a, family, socket_type, socket_rule, explicit_on_ip) open_sockets = [] relevant = False success = True for addrs in sockets: for addr in addrs: print "### Opening listener %s" % (addr,) l_sock = open_listener(a, family, socket_type, addr) open_sockets.append(l_sock) c_sock = socket.socket(family, socket_type) c_sock.settimeout(2) if family == socket.AF_INET: target_ip = target_ip4 elif family == socket.AF_INET6: target_ip = target_ip6 else: raise Exception, "Unsupported protocol family; family='%d'" % family print "### Connecting to %s" % ((target_ip, target_port),) try: if relevant or not skip_irrelevant: c_sock.connect((target_ip, target_port)) if socket_type == socket.SOCK_DGRAM: c_sock.send("almafa") print "### Connected to %s" % ((target_ip, target_port),) else: print "### Skipped connection to %s" % ((target_ip, target_port),) c_sock = None except socket.error: # connection failed c_sock = None print "### Connection failed to %s" % ((target_ip, target_port),) if relevant or not skip_irrelevant: print "### Waiting for connection %s" % (addr,) (r, w, x) = a.select(open_sockets, [], [], timeout=2) else: (r, w, x) = ([], [], []) if socket_type == socket.SOCK_DGRAM and (len(r) + len(w) + len(x)) == 0: print "### Datagram read failed on %s" % (addr,) c_sock = None if socket_type == socket.SOCK_STREAM and c_sock != None and (len(r) + len(w) + len(x)) != 1: print r, w, x print "FAILED: connected and select returned no connection?" success = False elif c_sock == None: # timed out if not relevant: print "PASSED: %s, didn't get a connection on irrelevant address" % (addr,) else: print "FAILED: %s, didn't get a connection but we should have" % (addr,) success = False else: if len(r) != 1: print "FAILED: uhh, we got a connection on multiple fds?" success = False else: if not relevant: print "FAILED: %s, we got a connection but we shouldn't have" % (addr,) success = False else: if r[0] == l_sock: print "PASSED: %s, we got a connection as we deserved" % (addr,) if socket_type == socket.SOCK_STREAM: a_sock = l_sock.accept() else: print "FAILED: %s, we got the connection on the wrong listener" % (addr,) success = False if len(addr) == 3: # we close the socket if it refers to the zero address as # otherwise we'd have a bind conflict, as the upcoming bind # address will contain a more specific version of this # listener l_sock = None open_sockets = open_sockets[:-1] r, w, x = ([], [], []) relevant = True return success def run_testcases(a, all_sockets): result = True for family in (socket.AF_INET, socket.AF_INET6): for socket_type in (socket.SOCK_DGRAM, socket.SOCK_STREAM): for socket_rule in (False, True): for explicit_on_ip in (False, True): if not run_sockets(a, family, socket_type, socket_rule, explicit_on_ip, all_sockets[(family, socket_rule)]): result = False print "=========================" if result: print "All tests PASSED" else: print "Some tests FAILED" return result # testcases # TPROXY rule only, no "socket" match # 80 -> 50080 redirection rule # TCP listener on redirect-ip:50080, connection establishes # TCP listener on redirect-ip:80, connection does not establish # TCP listener on redirect-ip:80 & redirect-ip:50080, connection goes to the latter # TCP listener on 0.0.0.0:50080, connection establishes # TCP listener on 0.0.0.0:50080 & redirect-ip:50080, connection establishes to the latter # TCP listener on 0.0.0.0:80, connection does not establish # TCP listener on 0.0.0.0:80 & 0.0.0.0:50080, connection goes to the latter # TCP listener on target-ip:80, connection does not establish # TCP listener on target-ip:50080, connection does not establish def parse_parameters(): global proxy_ip4, target_ip4, proxy_ip6, target_ip6 try: opts, args = getopt.getopt(sys.argv[1:], "p:t:P:T:", ["proxy=", "target=", "proxy6=", "target6="]) except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" sys.exit(2) output = None verbose = False for o, a in opts: if o in ("-p", "--proxy"): proxy_ip4 = a elif o in ("-t", "--target"): target_ip4 = a elif o in ("-P", "--proxy6"): proxy_ip6 = a elif o in ("-T", "--target6"): target_ip6 = a else: assert False, "unhandled option" def setup_route(): retcode = 0 print "Setting up IPv4 route" subprocess.call(["ip", "route" , "del", "%s/32" % target_ip4]) retcode = retcode + subprocess.call(["ip", "route" , "add", "%s/32" % target_ip4, "via", proxy_ip4]) print "Setting up IPv6 route" subprocess.call(["ip", "-6", "route" , "del", "%s/64" % target_ip6]) retcode = retcode + subprocess.call(["ip", "-6", "route" , "add", "%s/64" % target_ip6, "via", proxy_ip6]) return retcode def main(): parse_parameters() print ("Started with addresses:\n%s\n%s\n%s\n%s\n" % (proxy_ip4, target_ip4, proxy_ip6, target_ip6)) route_status = setup_route() try: a = connect_agent() #print a.iptables_save(family=socket.AF_INET) run_testcases(a, testcases()) if route_status != 0: print ("Route setup was not completely succesful") a.quit() return 0 except Exception, e: traceback.print_exc() print e return 1 sys.exit(main()) zorp-3.9.5/tests/tools/udp6_accept.c000066400000000000000000000033221172670260400173720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #define PORT 12345 int make_socket (uint16_t port) { int sock, flag = 1; struct sockaddr_in6 name; /* Create the socket. */ sock = socket(PF_INET6, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } /* Set the reuse flag. */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) { perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); exit(EXIT_FAILURE); } /* Give the socket a name. */ name.sin6_family = AF_INET6; name.sin6_port = htons(port); memcpy(&name.sin6_addr, &in6addr_any, sizeof(in6addr_any)); if (bind(sock, (struct sockaddr *) &name, sizeof (name)) < 0) { perror("bind"); exit(EXIT_FAILURE); } return sock; } int main(void) { int sock, new; struct sockaddr_in6 clientname; unsigned char buf[256]; socklen_t size; sock = make_socket(PORT); fprintf(stderr, "Receiving on port %d\n", PORT); if (recv(sock, buf, sizeof(buf), MSG_PEEK) < 0) { perror("recv"); exit(EXIT_FAILURE); } size = sizeof(clientname); new = accept(sock, (struct sockaddr *) &clientname, &size); if (new < 0) { perror("accept"); exit(EXIT_FAILURE); } fprintf(stderr, "Connect from %s:%hu\n", inet_ntop(AF_INET6, &clientname.sin6_addr, buf, sizeof(buf)), ntohs(clientname.sin6_port)); if (read(new, buf, 1) < 0) { perror("read"); exit(EXIT_FAILURE); } fprintf(stderr, "Byte received: 0x%x\n", buf[0]); close(new); close(sock); exit(EXIT_SUCCESS); } zorp-3.9.5/tests/tools/udp_accept.c000066400000000000000000000031711172670260400173060ustar00rootroot00000000000000#include #include #include #include #include #include #include #define PORT 12345 int make_socket (uint16_t port) { int sock, flag = 1; struct sockaddr_in name; /* Create the socket. */ sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); exit(EXIT_FAILURE); } /* Set the reuse flag. */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0) { perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); exit(EXIT_FAILURE); } /* Give the socket a name. */ name.sin_family = AF_INET; name.sin_port = htons(port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock, (struct sockaddr *) &name, sizeof (name)) < 0) { perror("bind"); exit(EXIT_FAILURE); } return sock; } int main(void) { int sock, new; struct sockaddr_in clientname; unsigned char buf[256]; socklen_t size; sock = make_socket(PORT); fprintf(stderr, "Receiving on port %d\n", PORT); if (recv(sock, buf, sizeof(buf), MSG_PEEK) < 0) { perror("recv"); exit(EXIT_FAILURE); } size = sizeof(clientname); new = accept(sock, (struct sockaddr *) &clientname, &size); if (new < 0) { perror("accept"); exit(EXIT_FAILURE); } fprintf(stderr, "Connect from %s:%hu\n", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port)); if (read(new, buf, 1) < 0) { perror("read"); exit(EXIT_FAILURE); } fprintf(stderr, "Byte received: 0x%x\n", buf[0]); close(new); close(sock); exit(EXIT_SUCCESS); } zorp-3.9.5/tests/unit/000077500000000000000000000000001172670260400146505ustar00rootroot00000000000000zorp-3.9.5/tests/unit/Makefile.am000066400000000000000000000005711172670260400167070ustar00rootroot00000000000000LIBS = @ZORP_LIBS@ @MODULES_LIBS@ check_PROGRAMS = pystruct test_audit test_szig test_dynexpect test_ipv6_radix pystruct_SOURCES = pystruct.c test_audit_SOURCES = test_audit.c test_szig_SOURCES = test_szig.c test_dynexpect_SOURCES = test_dynexpect.c test_ipv6_radix_SOURCES = test_ipv6_radix.c TESTS = pystruct test_audit test_szig test_ipv6_radix EXTRA_DIST = pystruct.py zorp-3.9.5/tests/unit/pystruct.c000066400000000000000000000111721172670260400167130ustar00rootroot00000000000000#include #include #include #include #include #include /* copied from lib/policy.c, should be changed whenever that one changes */ // struct _ZPolicy // { // gint ref_cnt; // gchar *policy_filename; // ZPolicyThread *main_thread; // }; gboolean call_test_func(const gchar *name, PyObject *args) { PyObject *main_module, *test_func, *res; gboolean success = FALSE; main_module = PyImport_AddModule("__main__"); test_func = PyObject_GetAttrString(main_module, (char *) name); res = PyObject_CallObject(test_func, args); Py_XDECREF(test_func); Py_XDECREF(args); if (res && z_policy_var_parse(res, "i", &success)) { /* init successful */ } else if (!res) { PyErr_Print(); } Py_XDECREF(res); if (!success) { fprintf(stderr, "Python test function failed: %s\n", name); exit(3); } return TRUE; } void test_sockaddr(void) { ZSockAddr *sa; ZPolicyObj *sa_obj; sa = z_sockaddr_inet_new("192.168.1.1", 59999); sa_obj = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); call_test_func("test_sockaddr", z_policy_var_build("(O)", sa_obj)); z_policy_var_unref(sa_obj); } gint counter = 0; ZPolicyObj * test_custom_get_value(gpointer user_data, const gchar *name , gpointer value) { g_assert(user_data == (gpointer) 0xdeadbabe); g_assert(value == (gpointer) 0xaaffaaff); (void) name; return PyInt_FromLong(counter++); } gint test_custom_set_value(gpointer user_data, const gchar *name, gpointer value, ZPolicyObj *new_value) { (void) user_data; (void) name; (void) value; (void) new_value; counter = 0; return 0; } void test_dict(void) { ZPolicyDict *dict; ZPolicyObj *str; gint simple_int; GString *simple_str; struct in_addr simple_ip; ZPolicyObj *simple_obj; gchar simple_cstr[64] = "huligan"; ZSockAddr *sa; simple_int = 55555; simple_str = g_string_new("abcdef"); inet_aton("192.168.5.6", &simple_ip); sa = z_sockaddr_inet_new("192.168.1.1", 59999); simple_obj = z_policy_sockaddr_new(sa); z_sockaddr_unref(sa); dict = z_policy_dict_new(); z_policy_dict_register(dict, Z_VT_INT, "simple_int", Z_VF_RW, &simple_int); z_policy_dict_register(dict, Z_VT_INT, "literal_int", Z_VF_RW | Z_VF_LITERAL, 66666); z_policy_dict_register(dict, Z_VT_STRING, "simple_str", Z_VF_RW, simple_str); z_policy_dict_register(dict, Z_VT_STRING, "literal_str", Z_VF_RW | Z_VF_LITERAL, "abrakadabra"); z_policy_dict_register(dict, Z_VT_CSTRING, "simple_cstr", Z_VF_RW, simple_cstr, sizeof(simple_cstr)); z_policy_dict_register(dict, Z_VT_CSTRING, "literal_cstr_ro", Z_VF_READ | Z_VF_LITERAL, "viharkeszulodik", 64); z_policy_dict_register(dict, Z_VT_CSTRING, "literal_cstr", Z_VF_RW | Z_VF_LITERAL | Z_VF_DUP, "viharkeszulodik2", 64); z_policy_dict_register(dict, Z_VT_IP, "simple_ip", Z_VF_RW, &simple_ip); z_policy_dict_register(dict, Z_VT_IP, "simple_ip_str", Z_VF_RW | Z_VF_IP_STR, &simple_ip); z_policy_dict_register(dict, Z_VT_ALIAS, "alias", Z_VF_RW, "simple_str"); z_policy_dict_register(dict, Z_VT_OBJECT, "simple_obj", Z_VF_RW, &simple_obj); // get, set, free, user_data, user_data_free z_policy_dict_register(dict, Z_VT_CUSTOM, "custom", Z_VF_RW, (gpointer) 0xaaffaaff, test_custom_get_value, test_custom_set_value, NULL, (gpointer) 0xdeadbabe, NULL); /* Z_VT_IP6, Z_VT_HASH, Z_VT_METHOD, Z_VT_DIMHASH, */ str = z_policy_struct_new(dict, Z_PST_SHARED); call_test_func("test_dict", z_policy_var_build("(O)", str)); z_policy_var_unref(str); } int main() { gchar *srcdir = getenv("srcdir"); gchar policy_file[512]; ZPolicy *policy; FILE *script; g_snprintf(policy_file, sizeof(policy_file), "%s/pystruct.py", srcdir ? srcdir : "."); z_thread_init(); if (!z_python_init()) { fprintf(stderr, "Python initialization failed\n"); return 1; } policy = z_policy_new(policy_file); z_policy_boot(policy); script = fopen(policy->policy_filename, "r"); if (!script) { fprintf(stderr, "Error loading test script\n"); return 1; } z_policy_thread_acquire(policy->main_thread); if (PyRun_SimpleFile(script, policy->policy_filename) == -1) { fprintf(stderr, "Parsing failed\n"); fclose(script); return 2; } fclose(script); test_sockaddr(); test_dict(); z_policy_thread_release(policy->main_thread); //z_policy_unref(policy); z_thread_destroy(); z_python_destroy(); return 0; } zorp-3.9.5/tests/unit/pystruct.py000066400000000000000000000051521172670260400171220ustar00rootroot00000000000000import socket def test_attr_ro(o, attr_name, default_value): if getattr(o, attr_name) != default_value: print "bad default value for attr %s, value %s, default %s" % (attr_name, getattr(o, attr_name), default_value) raise ValueError def test_attr_rw(o, attr_name, default_value, mod_value): test_attr_ro(o, attr_name, default_value) setattr(o, attr_name, mod_value) if getattr(o, attr_name) != mod_value: print "bad changed value for attr %s, value %s, default %s" % (attr_name, getattr(o, attr_name), mod_value) raise ValueError def test_sockaddr(sa): test_attr_ro(sa, 'family', 2) test_attr_ro(sa, 'type', 'inet') test_attr_ro(sa, 'ip', socket.htonl(0xc0a80101)) test_attr_rw(sa, 'ip_s', '192.168.1.1', '192.168.2.1') test_attr_ro(sa, 'ip', socket.htonl(0xc0a80201)) test_attr_rw(sa, 'port', 59999, 60000) try: sa.type = 'qqq' raise ValueError except AttributeError: pass try: sa.family = 5 raise ValueError except AttributeError: pass if sa.format() != 'AF_INET(192.168.2.1:60000)': return 0 if sa.clone(0).format() != 'AF_INET(192.168.2.1:60000)': return 0 clone = sa.clone(0) if not sa.equal(clone): return 0 clone.port = 55555 if sa.equal(clone): return 0 return 1 def test_dict(d): test_attr_rw(d, 'simple_int', 55555, 44444) test_attr_rw(d, 'literal_int', 66666, 77777) test_attr_rw(d, 'simple_str', 'abcdef', 'kukutyin1') test_attr_rw(d, 'literal_str', 'abrakadabra', 'kukutyin2') test_attr_rw(d, 'simple_cstr', 'huligan', 'kukutyin3') test_attr_ro(d, 'literal_cstr_ro', 'viharkeszulodik') test_attr_rw(d, 'literal_cstr', 'viharkeszulodik2', 'kismacska3') test_attr_rw(d, 'simple_ip', socket.htonl(0xc0a80506), socket.htonl(0xc0a80508)) test_attr_rw(d, 'simple_ip_str', '192.168.5.8', '192.168.8.9') test_attr_ro(d, 'simple_ip', socket.htonl(0xc0a80809)) test_attr_rw(d, 'alias', 'kukutyin1', 'masvalami') test_sockaddr(d.simple_obj) if not (d.custom == 0 and d.custom == 1 and d.custom == 2): raise ValueError d.custom = 55 if not (d.custom == 0 and d.custom == 1 and d.custom == 2): raise ValueError return 1 zorp-3.9.5/tests/unit/test_audit.c000066400000000000000000000000411172670260400171540ustar00rootroot00000000000000 int main(void) { return 0; } zorp-3.9.5/tests/unit/test_bllookup.c000066400000000000000000000000001172670260400176700ustar00rootroot00000000000000zorp-3.9.5/tests/unit/test_dynexpect.c000066400000000000000000000166651172670260400200740ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include static int sock; #define CLEAR(m) memset(&m, 0, sizeof(m)) /* checks */ static void test_availability(void) { struct ip_ct_dynexpect_map map = { .mapping_id = 0x7fffffff, }; socklen_t len = sizeof(map); g_assert(getsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &map, &len) == -1); g_assert(errno == ENOENT); } static void test_mapping_api(void) { /* create a mapping */ struct ip_ct_dynexpect_map map = { .proto = IPPROTO_TCP, .orig_ip = 0x12345678, .orig_port = 256, .new_ip = 0x12345676, .new_port = 255, .n_ports = 2, }; socklen_t len = sizeof(map); g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &map, len) == 0); /* query mapping */ struct ip_ct_dynexpect_map rmap = { .mapping_id = map.mapping_id, }; g_assert(getsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &rmap, &len) == 0); g_assert(len == sizeof(rmap)); g_assert(map.mapping_id == rmap.mapping_id); g_assert(memcmp(&map, &rmap, sizeof(map)) == 0); /* set mark */ struct ip_ct_dynexpect_mark mark = { .mapping_id = rmap.mapping_id, .mark = 0xdeadbeef, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_MARK, &mark, sizeof(mark)) == 0); /* create expectation */ struct ip_ct_dynexpect_expect expect = { .mapping_id = rmap.mapping_id, .peer_ip = 0x23456789, .peer_port = 50000, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_EXPECT, &expect, sizeof(expect)) == 0); /* destroy mapping */ struct ip_ct_dynexpect_destroy destroy = { .mapping_id = map.mapping_id, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_DESTROY, &destroy, sizeof(destroy)) == 0); } static void test_mapping_timeout(void) { /* create a mapping */ struct ip_ct_dynexpect_map map = { .proto = IPPROTO_TCP, .orig_ip = 0x12345678, .orig_port = 256, .new_ip = 0x12345676, .new_port = 255, .n_ports = 2, }; socklen_t len = sizeof(map); g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &map, len) == 0); /* query mapping */ struct ip_ct_dynexpect_map rmap = { .mapping_id = map.mapping_id, }; g_assert(getsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &rmap, &len) == 0); g_assert(len == sizeof(rmap)); g_assert(map.mapping_id == rmap.mapping_id); g_assert(memcmp(&map, &rmap, sizeof(map)) == 0); /* create expectation */ struct ip_ct_dynexpect_expect expect = { .mapping_id = rmap.mapping_id, .peer_ip = 0x23456789, .peer_port = 50000, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_EXPECT, &expect, sizeof(expect)) == 0); /* check if the expectations were actually created */ int res = system("grep -q -E 'l3proto = 2 proto=6 src=18.52.86.120 dst=35.69.103.137 sport=256 dport=50000 dynexpect' /proc/net/nf_conntrack_expect"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 0)); res = system("grep -q -E 'l3proto = 2 proto=6 src=18.52.86.120 dst=35.69.103.137 sport=257 dport=50001 dynexpect' /proc/net/nf_conntrack_expect"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 0)); /* wait for timeout */ sleep(7); /* check that the expectations have been removed */ res = system("grep -q dynexpect /proc/net/nf_conntrack_expect"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 1)); /* destroy mapping */ struct ip_ct_dynexpect_destroy destroy = { .mapping_id = map.mapping_id, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_DESTROY, &destroy, sizeof(destroy)) < 0); } static void test_expected_connection(void) { /* create a mapping */ struct ip_ct_dynexpect_map map = { .proto = IPPROTO_UDP, .orig_ip = 0x7f000001, .orig_port = 34000, .new_ip = 0x7f000002, .new_port = 0, .n_ports = 2, }; socklen_t len = sizeof(map); g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &map, len) == 0); /* query mapping */ struct ip_ct_dynexpect_map rmap = { .mapping_id = map.mapping_id, }; g_assert(getsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &rmap, &len) == 0); g_assert(len == sizeof(rmap)); g_assert(map.mapping_id == rmap.mapping_id); g_assert(rmap.n_active == 0); /* set mark */ struct ip_ct_dynexpect_mark mark = { .mapping_id = rmap.mapping_id, .mark = 0xbeef, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_MARK, &mark, sizeof(mark)) == 0); /* create expectation */ struct ip_ct_dynexpect_expect expect = { .mapping_id = rmap.mapping_id, .peer_ip = 0x7f000003, .peer_port = 50000, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_EXPECT, &expect, sizeof(expect)) == 0); /* create connection */ int sock = socket(PF_INET, SOCK_DGRAM, 0); g_assert(sock > 0); struct sockaddr_in name = { .sin_family = AF_INET, .sin_port = htons(34000), .sin_addr.s_addr = htonl(0x7f000001) }; g_assert(bind(sock, (struct sockaddr *) &name, sizeof(name)) == 0); struct sockaddr_in dest = { .sin_family = AF_INET, .sin_port = htons(50000), .sin_addr.s_addr = htonl(0x7f000003) }; int res = sendto(sock, &len, sizeof(len), 0, (struct sockaddr *) &dest, sizeof(dest)); if (res < 0) { perror("sendto"); g_assert_not_reached(); } /* poll the mapping: check that it does not time out and that it has exactly * one media stream */ for (int i = 0; i < 8; i++) { /* query mapping */ struct ip_ct_dynexpect_map rmap = { .mapping_id = map.mapping_id }; sleep(1); g_assert(getsockopt(sock, SOL_IP, SO_DYNEXPECT_MAP, &rmap, &len) == 0); g_assert(len == sizeof(rmap)); g_assert(rmap.n_active == 1); } /* check mark value */ res = system("grep -q -E 'src=127.0.0.1 dst=127.0.0.3 sport=34000 dport=50000 packets=1.*src=127.0.0.3 dst=127.0.0.2 sport=50000.*mark=48879' /proc/net/nf_conntrack"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 0)); /* wait for timeout */ sleep(6); /* check that the connection tracking entry was removed on timeout */ res = system("grep -q -E 'src=127.0.0.1 dst=127.0.0.3 sport=34000 dport=50000 packets=1.*src=127.0.0.3 dst=127.0.0.2 sport=50000' /proc/net/nf_conntrack"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 1)); /* check that the expectations have been removed */ res = system("grep -q dynexpect /proc/net/nf_conntrack_expect"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 1)); /* destroy mapping: should fail because the mapping already timed out */ struct ip_ct_dynexpect_destroy destroy = { .mapping_id = rmap.mapping_id, }; g_assert(setsockopt(sock, SOL_IP, SO_DYNEXPECT_DESTROY, &destroy, sizeof(destroy)) < 0); } int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); sock = socket(AF_INET, SOCK_DGRAM, 0); g_assert_cmpint(sock, >, 0); /* set mapping timeout to 5 seconds for the duration of the test */ int res = system("echo 5 > /sys/module/nf_conntrack_dynexpect/parameters/mapping_timeout"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 0)); g_test_add_func("/dynexpect/availability", test_availability); g_test_add_func("/dynexpect/mapping_api", test_mapping_api); g_test_add_func("/dynexpect/timeout", test_mapping_timeout); g_test_add_func("/dynexpect/expected_connection", test_expected_connection); g_test_run(); /* reset mapping timeout to the default */ res = system("echo 300 > /sys/module/nf_conntrack_dynexpect/parameters/mapping_timeout"); g_assert(WIFEXITED(res) && (WEXITSTATUS(res) == 0)); return 0; } zorp-3.9.5/tests/unit/test_ipv6_radix.c000066400000000000000000000250031172670260400201260ustar00rootroot00000000000000#include #include #include #include #include /************************************************************************/ /************************************************************************/ /************************************************************************/ /* These are routinges copied from the Linux kernel to be able to * compile the radix tree code in user-space. Obviously these are * under GPL. */ #ifndef __KERNEL__ typedef guint32 __be32; typedef guint16 __u16; /** * fls - find last set bit in word * @x: the word to search * * This is defined in a similar way as the libc and compiler builtin * ffs, but returns the position of the most significant set bit. * * fls(value) returns 0 if value is 0 or the position of the last * set bit if value is nonzero. The last (most significant) bit is * at position 32. */ static inline int fls(int x) { int r; asm("bsrl %1,%0\n\t" "cmovzl %2,%0" : "=&r" (r) : "rm" (x), "rm" (-1)); return r + 1; } static inline void ipv6_addr_copy(struct in6_addr *a1, const struct in6_addr *a2) { memcpy(a1, a2, sizeof(struct in6_addr)); } static inline int __ipv6_prefix_equal(const __be32 *a1, const __be32 *a2, unsigned int prefixlen) { unsigned pdw, pbi; /* check complete u32 in prefix */ pdw = prefixlen >> 5; if (pdw && memcmp(a1, a2, pdw << 2)) return 0; /* check incomplete u32 in prefix */ pbi = prefixlen & 0x1f; if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi)))) return 0; return 1; } static inline int ipv6_prefix_equal(const struct in6_addr *a1, const struct in6_addr *a2, unsigned int prefixlen) { return __ipv6_prefix_equal(a1->s6_addr32, a2->s6_addr32, prefixlen); } /* * find the first different bit between two addresses * length of address must be a multiple of 32bits */ static inline int __ipv6_addr_diff(const void *token1, const void *token2, int addrlen) { const __be32 *a1 = token1, *a2 = token2; int i; addrlen >>= 2; for (i = 0; i < addrlen; i++) { __be32 xb = a1[i] ^ a2[i]; if (xb) return i * 32 + 32 - fls(ntohl(xb)); } /* * we should *never* get to this point since that * would mean the addrs are equal * * However, we do get to it 8) And exacly, when * addresses are equal 8) * * ip route add 1111::/128 via ... * ip route add 1111::/64 via ... * and we are here. * * Ideally, this function should stop comparison * at prefix length. It does not, but it is still OK, * if returned value is greater than prefix length. * --ANK (980803) */ return (addrlen << 5); } static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_addr *a2) { return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); } /* Fake kzorp structures/code */ struct kz_zone {}; static inline void kz_zone_put(struct kz_zone *z G_GNUC_UNUSED) {}; #endif /************************************************************************/ /************************************************************************/ /************************************************************************/ #include "kzorp_radix.c" /************************************************************************/ /************************************************************************/ /************************************************************************/ static struct in6_addr * string_as_address_v6(const char *src) { static struct in6_addr _buf; g_assert(inet_pton(AF_INET6, src, &_buf)); return &_buf; } static struct in_addr * string_as_address_v4(const char *src) { static struct in_addr _buf; g_assert(inet_pton(AF_INET, src, &_buf)); return &_buf; } static const char * address_as_string(const struct in6_addr *addr) { static char _buf[256]; g_assert(inet_ntop(AF_INET6, addr, _buf, sizeof(_buf))); return _buf; } static void __print_node(GString *str, int level, const struct kz_lookup_ipv6_node *node) { if (node->zone) g_string_append_printf(str, "%*d|%s/%d -> '%s'\n", 2 * level, level, address_as_string(&node->addr), node->prefix_len, (char *)node->zone); else g_string_append_printf(str, "%*d|%s/%d\n", 2 * level, level, address_as_string(&node->addr), node->prefix_len); } static void __print_tree(GString *str, int level, const struct kz_lookup_ipv6_node *node) { if (node == NULL) return; __print_node(str, level, node); __print_tree(str, level + 1, node->left); __print_tree(str, level + 1, node->right); } static const char * tree_as_string(const struct kz_lookup_ipv6_node *root){ static GString *_str = NULL; if (_str == NULL) { _str = g_string_new(""); } g_string_assign(_str, ""); __print_tree(_str, 0, root); return _str->str; } #define TREE_NEW(root) do { if (root) ipv6_destroy(root); root = ipv6_node_new(); } while (0); #define TREE_ADD(root, net, prefix) ipv6_add(root, string_as_address_v6(net), prefix) #define TREE_ADD_DATA(root, net, prefix, data) \ do { \ struct kz_lookup_ipv6_node *n = ipv6_add(root, string_as_address_v6(net), prefix); \ if (n) \ n->zone= (struct kz_zone *) data; \ } while (0); #define TREE_PRINT(root) printf("%s", tree_as_string(root)) #define TREE_CHECK(root, str) do { if (g_test_verbose()) TREE_PRINT(root);\ g_assert_cmpstr(tree_as_string(root), ==, str); } while (0); #define TREE_LOOKUP(root, address, expected) \ do { \ struct kz_lookup_ipv6_node *n = ipv6_lookup(root, string_as_address_v6(address)); \ g_assert(n != NULL); \ g_assert_cmpstr((char *)n->zone, ==, expected); \ } while (0); #define TREE_LOOKUP_FAILS(root, address) \ do { \ struct kz_lookup_ipv6_node *n = ipv6_lookup(root, string_as_address_v6(address)); \ g_assert(n == NULL || n->zone == NULL); \ } while (0); static void test_print(void) { struct kz_lookup_ipv6_node *root = ipv6_node_new(); TREE_CHECK(root, "0|::/0\n"); TREE_ADD(root, "::", 32); TREE_ADD(root, "::", 64); TREE_ADD(root, "ffff::", 16); TREE_ADD(root, "ffff:ff00::", 32); TREE_ADD(root, "ffff:f000::", 32); TREE_CHECK(root, "0|::/0\n" " 1|::/32\n" " 2|::/64\n" " 1|ffff::/16\n" " 2|ffff:f000::/20\n" " 3|ffff:f000::/32\n" " 3|ffff:ff00::/32\n"); ipv6_destroy(root); } static void test_add(void) { struct kz_lookup_ipv6_node *root = NULL; /* construct an empty tree */ TREE_NEW(root); TREE_CHECK(root, "0|::/0\n"); /* postfix insertion */ TREE_ADD(root, "ffff::", 15); TREE_ADD(root, "ffff:ffff::", 31); TREE_CHECK(root, "0|::/0\n" " 1|ffff::/15\n" " 2|ffff:ffff::/31\n"); TREE_NEW(root); TREE_ADD(root, "::", 15); TREE_ADD(root, "::", 31); TREE_CHECK(root, "0|::/0\n" " 1|::/15\n" " 2|::/31\n"); /* inserting shorter prefix */ TREE_NEW(root); TREE_ADD(root, "ffff:ffff::", 31); TREE_ADD(root, "ffff::", 15); TREE_CHECK(root, "0|::/0\n" " 1|ffff::/15\n" " 2|ffff:ffff::/31\n"); TREE_NEW(root); TREE_ADD(root, "::", 31); TREE_ADD(root, "::", 15); TREE_CHECK(root, "0|::/0\n" " 1|::/15\n" " 2|::/31\n"); /* same prefix length, but different prefix */ TREE_NEW(root); TREE_ADD(root, "ffff::", 16); TREE_ADD(root, "f0ff::", 16); TREE_CHECK(root, "0|::/0\n" " 1|f0ff::/4\n" " 2|f0ff::/16\n" " 2|ffff::/16\n"); TREE_NEW(root); TREE_ADD(root, "00ff::", 16); TREE_ADD(root, "0fff::", 16); TREE_CHECK(root, "0|::/0\n" " 1|fff::/4\n" " 2|ff::/16\n" " 2|fff::/16\n"); /* adding a node already present */ TREE_NEW(root); TREE_ADD(root, "fe80::", 10); TREE_ADD(root, "fe80::", 10); TREE_ADD(root, "fe8f::", 10); TREE_CHECK(root, "0|::/0\n" " 1|fe80::/10\n"); ipv6_destroy(root); } static void test_lookup(void) { struct kz_lookup_ipv6_node *root = NULL; /* empty tree */ TREE_NEW(root); TREE_LOOKUP_FAILS(root, "::1"); /* add a single subnet */ TREE_NEW(root); TREE_ADD_DATA(root, "fe80::", 10, "link-local"); TREE_LOOKUP(root, "fe80:1::", "link-local"); TREE_LOOKUP_FAILS(root, "::1"); TREE_LOOKUP_FAILS(root, "fe00::"); /* check best match */ TREE_NEW(root); TREE_ADD_DATA(root, "::f000", 116, "subnet1"); TREE_LOOKUP(root, "::ffff", "subnet1"); TREE_ADD_DATA(root, "::f800", 117, "subnet11"); TREE_LOOKUP(root, "::ffff", "subnet11"); TREE_ADD_DATA(root, "::f000", 117, "subnet12"); TREE_LOOKUP(root, "::ffff", "subnet11"); TREE_LOOKUP(root, "::f0ff", "subnet12"); /* exact match */ TREE_ADD_DATA(root, "::ffff", 128, "exact1"); TREE_LOOKUP(root, "::ffff", "exact1"); TREE_ADD_DATA(root, "::fffe", 128, "exact2"); TREE_LOOKUP(root, "::ffff", "exact1"); TREE_LOOKUP(root, "::fffe", "exact2"); TREE_LOOKUP(root, "::fff0", "subnet11"); ipv6_destroy(root); } /************************************************************************/ /************************************************************************/ /************************************************************************/ #include "kzorp_mask.c" /************************************************************************/ /************************************************************************/ /************************************************************************/ #define TEST_MASK(mask, length) do { g_assert_cmpint(mask_to_size_v4(string_as_address_v4(mask)), ==, length); } while (0); static void test_mask_v4(void) { TEST_MASK("0.0.0.0", 0); TEST_MASK("128.0.0.0", 1); TEST_MASK("255.255.255.0", 24); TEST_MASK("255.255.255.128", 25); TEST_MASK("255.255.255.255", 32); } #undef TEST_MASK #define TEST_MASK(mask, length) do { g_assert_cmpint(mask_to_size_v6(string_as_address_v6(mask)), ==, length); } while (0); static void test_mask_v6(void) { TEST_MASK("::", 0); TEST_MASK("8000::", 1); TEST_MASK("f000::", 4); TEST_MASK("fffe::", 15); TEST_MASK("ffff::", 16); TEST_MASK("ffff:8000::", 17); TEST_MASK("ffff:c000::", 18); TEST_MASK("ffff:ffff::", 32); TEST_MASK("ffff:ffff:8000::", 33); TEST_MASK("ffff:ffff:ffff:ffff:ffff:fffe:0000:0000", 95); TEST_MASK("ffff:ffff:ffff:ffff:ffff:ffff:0000:0000", 96); TEST_MASK("ffff:ffff:ffff:ffff:ffff:ffff:8000:0000", 97); TEST_MASK("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0000", 112); TEST_MASK("ffff:ffff:ffff:ffff:ffff:ffff:ffff:8000", 113); TEST_MASK("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128); } #undef TEST_MASK int main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); g_test_add_func("/radix/print", test_print); g_test_add_func("/radix/add", test_add); g_test_add_func("/radix/lookup", test_lookup); g_test_add_func("/util/mask_v4", test_mask_v4); g_test_add_func("/util/mask_v6", test_mask_v6); g_test_run(); return EXIT_SUCCESS; } zorp-3.9.5/tests/unit/test_szig.c000066400000000000000000000170571172670260400170410ustar00rootroot00000000000000#include #include #include #include /* * TODO: test per-zone service counters * TODO: test license address limit counters */ #define NUM_CONNS 900 #define NUM_THREADS 900 #define TICK_TIME 5 #define S_1_MIN 60 #define S_5_MIN (5 * S_1_MIN) #define S_15_MIN (15 * S_1_MIN) #define TICKS_PER_1_MIN (60 / TICK_TIME) #define TICKS_PER_5_MIN (300 / TICK_TIME) #define TICKS_PER_15_MIN (900 / TICK_TIME) static gint init_szig(void) { z_thread_init(); z_szig_init("test_szig"); return 0; } static gint check_szig_long(const char * const node_name, glong expected) { const ZSzigNode * const node = z_szig_tree_lookup(node_name, FALSE, NULL, NULL); if (!node || (node->value.type != Z_SZIG_TYPE_LONG)) { fprintf(stderr, "non-existent node or type mismatch; name='%s'\n", node_name); return 1; } if (node->value.u.long_value != expected) { fprintf(stderr, "value mismatch; name='%s', value='%ld', expected='%ld'\n", node_name, node->value.u.long_value, expected); return 1; } return 0; } static void generate_tick(guint tick) { GTimeVal time; time.tv_sec = tick * 5; time.tv_usec = 0; z_szig_event(Z_SZIG_TICK, z_szig_value_new_time(&time)); } static void forward_time(guint ticks) { static guint last = 0; guint i; for (i = 0; i < ticks; i++) generate_tick(last + i); last += ticks; } static void generate_connections(void) { guint i; fprintf(stdout, "generating connection events\n"); for (i = 0; i < NUM_CONNS; i++) { z_szig_event(Z_SZIG_SERVICE_COUNT, z_szig_value_new_props("test_service", "session_number", z_szig_value_new_long(i + 1), "sessions_running", z_szig_value_new_long(NUM_CONNS - i), NULL)); z_szig_event(Z_SZIG_CONNECTION_PROPS, z_szig_value_new_connection_props("test_service", i, 0, 0, "auth_user", "testuser", "auth_info", "", "auth_groups", "testgroup", "client_zone", "czone", "server_zone", "szone", NULL)); z_szig_event(Z_SZIG_CONNECTION_STOP, z_szig_value_new_connection_props("test_service", i, 0, 0, NULL)); } } static gint check_connection_rates(glong avg1, glong avg5, glong avg15) { gint failed = 0; fprintf(stdout, "checking connection rate averages; avg1='%ld', avg5='%ld', avg15='%ld'\n", avg1, avg5, avg15); failed = check_szig_long("service.test_service.session_number", NUM_CONNS); if (!failed) failed = check_szig_long("service.test_service.sessions_running", 1); if (!failed) failed = check_szig_long("service.test_service.sessions_max", NUM_CONNS); if (!failed) failed = check_szig_long("service.test_service.rate_max", NUM_CONNS / TICK_TIME); if (!failed) failed = check_szig_long("service.test_service.rate_avg1", avg1); if (!failed) failed = check_szig_long("service.test_service.rate_avg5", avg5); if (!failed) failed = check_szig_long("service.test_service.rate_avg15", avg15); return failed; } static int check_thread_counters(void) { fprintf(stdout, "testing thread counters\n"); /* preconditions */ if (check_szig_long("stats.thread_number", NUM_THREADS + 3)) return 1; if (check_szig_long("stats.threads_running", 1)) return 1; if (check_szig_long("stats.threads_max", 2)) return 1; z_szig_event(Z_SZIG_THREAD_START, NULL); sleep(1); if (check_szig_long("stats.thread_number", NUM_THREADS + 4)) return 1; if (check_szig_long("stats.threads_running", 2)) return 1; if (check_szig_long("stats.threads_max", 2)) return 1; z_szig_event(Z_SZIG_THREAD_START, NULL); sleep(1); if (check_szig_long("stats.thread_number", NUM_THREADS + 5)) return 1; if (check_szig_long("stats.threads_running", 3)) return 1; if (check_szig_long("stats.threads_max", 3)) return 1; z_szig_event(Z_SZIG_THREAD_STOP, NULL); sleep(1); if (check_szig_long("stats.thread_number", NUM_THREADS + 5)) return 1; if (check_szig_long("stats.threads_running", 2)) return 1; if (check_szig_long("stats.threads_max", 3)) return 1; z_szig_event(Z_SZIG_THREAD_STOP, NULL); sleep(1); if (check_szig_long("stats.thread_number", NUM_THREADS + 5)) return 1; if (check_szig_long("stats.threads_running", 1)) return 1; if (check_szig_long("stats.threads_max", 3)) return 1; return 0; } static void generate_threads(void) { guint i; fprintf(stdout, "generating threads\n"); for (i = 0; i < NUM_THREADS; i++) { z_szig_event(Z_SZIG_THREAD_START, NULL); z_szig_event(Z_SZIG_THREAD_STOP, NULL); } } static gint check_thread_rates(glong avg1, glong avg5, glong avg15) { gint failed = 0; /* the SZIG thread is always running and skews statistics */ fprintf(stdout, "checking thread rate averages; avg1='%ld', avg5='%ld', avg15='%ld'\n", avg1, avg5, avg15); failed = check_szig_long("stats.thread_number", NUM_THREADS + 3); if (!failed) failed = check_szig_long("stats.threads_running", 1); if (!failed) failed = check_szig_long("stats.threads_max", 2); if (!failed) failed = check_szig_long("stats.thread_rate_max", NUM_THREADS / TICK_TIME); if (!failed) failed = check_szig_long("stats.thread_rate_avg1", avg1); if (!failed) failed = check_szig_long("stats.thread_rate_avg5", avg5); if (!failed) failed = check_szig_long("stats.thread_rate_avg15", avg15); return failed; } int main(void) { if (init_szig()) return 1; /* szig thread count has a skew of 2 that offsets the threads started by Zorp core * before SZIG initialization: this makes testing thread counters and averages * difficult, so we generate two thread stop events here */ z_szig_event(Z_SZIG_THREAD_STOP, NULL); z_szig_event(Z_SZIG_THREAD_STOP, NULL); fprintf(stdout, "checking connection rate statistics\n"); /* generate NUM_CONNS connections right now */ generate_connections(); /* test sliding window */ fprintf(stdout, "fast-forwarding one minute\n"); forward_time(TICKS_PER_1_MIN); sleep(1); if (check_connection_rates(NUM_CONNS / S_1_MIN, NUM_CONNS / S_5_MIN, NUM_CONNS / S_15_MIN)) return 1; fprintf(stdout, "fast-forwarding four minutes\n"); forward_time(TICKS_PER_5_MIN - TICKS_PER_1_MIN); sleep(1); if (check_connection_rates(0, NUM_CONNS / S_5_MIN, NUM_CONNS / S_15_MIN)) return 1; fprintf(stdout, "fast-forwarding ten minutes\n"); forward_time(TICKS_PER_15_MIN - TICKS_PER_5_MIN); sleep(1); if (check_connection_rates(0, 0, NUM_CONNS / S_15_MIN)) return 1; fprintf(stdout, "fast-forwarding one minute\n"); forward_time(TICKS_PER_1_MIN); sleep(1); if (check_connection_rates(0, 0, 0)) return 1; fprintf(stdout, "checking thread rate statistics\n"); /* start and stop NUM_THREADS threads */ generate_threads(); /* test sliding window */ fprintf(stdout, "fast-forwarding one minute\n"); forward_time(TICKS_PER_1_MIN); sleep(1); if (check_thread_rates(NUM_THREADS / S_1_MIN, NUM_THREADS / S_5_MIN, NUM_THREADS / S_15_MIN)) return 1; fprintf(stdout, "checking thread counters\n"); if (check_thread_counters()) return 1; return 0; } zorp-3.9.5/zorp/000077500000000000000000000000001172670260400135215ustar00rootroot00000000000000zorp-3.9.5/zorp/Makefile.am000066400000000000000000000021111172670260400155500ustar00rootroot00000000000000pkglibdir=$(libdir)/zorp pkglibexecdir=$(libdir)/zorp LIBS=@ZORP_LIBS@ if PRO SUBDIRS = urlfilter endif pkglibexec_PROGRAMS = zorp zorp_SOURCES = main.c sysconf_DATA = policy.py.sample EXTRA_DIST = policy.py.sample logtags.txt logtags_gperf.c logtags_gperf.h logtags.gperf install-exec-local: mkdir -p $(DESTDIR)/$(sysconfdir) mkdir -p $(DESTDIR)/$(sysconfdir)/urlfilter chmod 0700 $(DESTDIR)/$(sysconfdir) mkdir -p $(DESTDIR)/$(pidfiledir) mkdir -p $(DESTDIR)/$(localstatedir)/quarantine mkdir -p $(DESTDIR)/$(localstatedir)/tmp mkdir -p $(DESTDIR)/$(localstatedir)/audit mkdir -p $(DESTDIR)/$(localstatedir)/urlfilter mkdir -p $(DESTDIR)/$(localstatedir)/cores main.c: logtags_gperf.c BUILT_SOURCES = logtags_gperf.c logtags_gperf.h logtags.gperf logtags_gperf.c: logtags.gperf logtags_gperf.h $(GPERF) -e ',' -t -N z_logtag_lookup_gperf $< > $@.T && mv $@.T $@ logtags_gperf.h: logtags.txt $(top_srcdir)/scripts/genlutable.sh header LOGTAG $< >$@.tmp && mv $@.tmp $@ logtags.gperf: logtags.txt $(top_srcdir)/scripts/genlutable.sh gperf LOGTAG $< >$@.tmp && mv $@.tmp $@ zorp-3.9.5/zorp/logtags.gperf000066400000000000000000000046701172670260400162150ustar00rootroot00000000000000/* automatically generated by genlutable.sh, do not edit directly */ %readonly-tables %define initializer-suffix ,0 struct tagid { char *name; int id; }; %% core.accounting, 1 core.auth, 2 core.debug, 3 core.dump, 4 core.error, 5 core.info, 6 core.license, 7 core.message, 8 core.policy, 9 core.session, 10 core.stderr, 11 finger.debug, 12 finger.error, 13 finger.policy, 14 finger.request, 15 finger.violation, 16 ftp.debug, 17 ftp.error, 18 ftp.policy, 19 ftp.reply, 20 ftp.request, 21 ftp.session, 22 ftp.violation, 23 http.accounting, 24 http.debug, 25 http.error, 26 http.policy, 27 http.request, 28 http.response, 29 http.violation, 30 imap.debug, 31 imap.error, 32 imap.info, 33 imap.policy, 34 imap.reply, 35 imap.request, 36 imap.violation, 37 ldap.debug, 38 ldap.error, 39 ldap.policy, 40 ldap.request, 41 ldap.response, 42 ldap.violation, 43 lp.debug, 44 lp.error, 45 lp.policy, 46 lp.request, 47 mime.error, 48 mime.policy, 49 mime.violation, 50 msrpc.debug, 51 msrpc.error, 52 msrpc.info, 53 msrpc.policy, 54 msrpc.session, 55 msrpc.violation, 56 nntp.debug, 57 nntp.policy, 58 nntp.reply, 59 nntp.request, 60 nntp.trace, 61 plug.debug, 62 plug.error, 63 plug.policy, 64 plug.session, 65 pop3.debug, 66 pop3.error, 67 pop3.policy, 68 pop3.reply, 69 pop3.request, 70 pop3.violation, 71 pssl.debug, 72 pssl.error, 73 pssl.policy, 74 radius.debug, 75 radius.error, 76 radius.policy, 77 radius.request, 78 radius.session, 79 radius.violation, 80 rdp.debug, 81 rdp.error, 82 rdp.info, 83 rdp.policy, 84 rdp.session, 85 rdp.violation, 86 rsh.accounting, 87 rsh.debug, 88 rsh.error, 89 rsh.policy, 90 rsh.violation, 91 satyr.error, 92 sip.violation, 93 sip.request, 94 sip.response, 95 sip.policy, 96 sip.debug, 97 sip.error, 98 sip.accounting, 99 smtp.accounting, 100 smtp.debug, 101 smtp.error, 102 smtp.info, 103 smtp.policy, 104 smtp.request, 105 smtp.response, 106 smtp.violation, 107 sqlnet.error, 108 sqlnet.request, 109 sqlnet.violation, 110 ssh.debug, 111 ssh.error, 112 ssh.policy, 113 ssh.violation, 114 ssh.accounting, 115 ssh.info, 116 telnet.debug, 117 telnet.error, 118 telnet.policy, 119 telnet.violation, 120 telnet.violations, 121 tftp.debug, 122 tftp.error, 123 tftp.policy, 124 tftp.request, 125 tftp.violation, 126 vnc.debug, 127 vnc.error, 128 vnc.info, 129 vnc.policy, 130 vnc.session, 131 vnc.violation, 132 whois.debug, 133 whois.error, 134 whois.request, 135 x11.debug, 136 x11.error, 137 x11.info, 138 x11.policy, 139 x11.session, 140 x11.violation, 141 zorp-3.9.5/zorp/logtags.txt000066400000000000000000000033051172670260400157230ustar00rootroot00000000000000core.accounting core.auth core.debug core.dump core.error core.info core.license core.message core.policy core.session core.stderr finger.debug finger.error finger.policy finger.request finger.violation ftp.debug ftp.error ftp.policy ftp.reply ftp.request ftp.session ftp.violation http.accounting http.debug http.error http.policy http.request http.response http.violation imap.debug imap.error imap.info imap.policy imap.reply imap.request imap.violation ldap.debug ldap.error ldap.policy ldap.request ldap.response ldap.violation lp.debug lp.error lp.policy lp.request mime.error mime.policy mime.violation msrpc.debug msrpc.error msrpc.info msrpc.policy msrpc.session msrpc.violation nntp.debug nntp.policy nntp.reply nntp.request nntp.trace plug.debug plug.error plug.policy plug.session pop3.debug pop3.error pop3.policy pop3.reply pop3.request pop3.violation pssl.debug pssl.error pssl.policy radius.debug radius.error radius.policy radius.request radius.session radius.violation rdp.debug rdp.error rdp.info rdp.policy rdp.session rdp.violation rsh.accounting rsh.debug rsh.error rsh.policy rsh.violation satyr.error sip.violation sip.request sip.response sip.policy sip.debug sip.error sip.accounting smtp.accounting smtp.debug smtp.error smtp.info smtp.policy smtp.request smtp.response smtp.violation sqlnet.error sqlnet.request sqlnet.violation ssh.debug ssh.error ssh.policy ssh.violation ssh.accounting ssh.info telnet.debug telnet.error telnet.policy telnet.violation telnet.violations tftp.debug tftp.error tftp.policy tftp.request tftp.violation vnc.debug vnc.error vnc.info vnc.policy vnc.session vnc.violation whois.debug whois.error whois.request x11.debug x11.error x11.info x11.policy x11.session x11.violation zorp-3.9.5/zorp/logtags_gperf.c000066400000000000000000000332141172670260400165130ustar00rootroot00000000000000/* C code produced by gperf version 3.0.3 */ /* Command-line: /usr/bin/gperf -e , -t -N z_logtag_lookup_gperf logtags.gperf */ /* Computed positions: -k'1-2,6,$' */ #if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) /* The character set is not based on ISO-646. */ error "gperf generated tables don't work with this execution character set. Please report a bug to ." #endif #line 4 "logtags.gperf" struct tagid { char *name; int id; }; #define TOTAL_KEYWORDS 141 #define MIN_WORD_LENGTH 8 #define MAX_WORD_LENGTH 17 #define MIN_HASH_VALUE 9 #define MAX_HASH_VALUE 318 /* maximum key range = 310, duplicates = 0 */ #ifdef __GNUC__ __inline #else #ifdef __cplusplus inline #endif #endif static unsigned int hash (str, len) register const char *str; register unsigned int len; { static const unsigned short asso_values[] = { 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 65, 319, 319, 319, 115, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 85, 145, 80, 100, 5, 90, 5, 70, 60, 45, 319, 110, 115, 15, 35, 10, 50, 0, 25, 0, 115, 15, 15, 110, 0, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319 }; return len + asso_values[(unsigned char)str[5]] + asso_values[(unsigned char)str[1]+1] + asso_values[(unsigned char)str[0]] + asso_values[(unsigned char)str[len - 1]]; } #ifdef __GNUC__ __inline #ifdef __GNUC_STDC_INLINE__ __attribute__ ((__gnu_inline__)) #endif #endif const struct tagid * z_logtag_lookup_gperf (str, len) register const char *str; register unsigned int len; { static const struct tagid wordlist[] = { {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 95 "logtags.gperf" {"rsh.error", 89}, {"",0}, {"",0}, {"",0}, {"",0}, #line 88 "logtags.gperf" {"rdp.error", 82}, {"",0}, {"",0}, #line 131 "logtags.gperf" {"tftp.request", 125}, {"",0}, #line 94 "logtags.gperf" {"rsh.debug", 88}, #line 129 "logtags.gperf" {"tftp.error", 123}, {"",0}, {"",0}, {"",0}, #line 87 "logtags.gperf" {"rdp.debug", 81}, #line 79 "logtags.gperf" {"pssl.error", 73}, #line 130 "logtags.gperf" {"tftp.policy", 124}, {"",0}, {"",0}, {"",0}, #line 75 "logtags.gperf" {"pop3.reply", 69}, #line 80 "logtags.gperf" {"pssl.policy", 74}, #line 76 "logtags.gperf" {"pop3.request", 70}, {"",0}, #line 118 "logtags.gperf" {"ssh.error", 112}, #line 73 "logtags.gperf" {"pop3.error", 67}, #line 91 "logtags.gperf" {"rdp.session", 85}, #line 114 "logtags.gperf" {"sqlnet.error", 108}, {"",0}, #line 115 "logtags.gperf" {"sqlnet.request", 109}, {"",0}, #line 74 "logtags.gperf" {"pop3.policy", 68}, {"",0}, {"",0}, #line 117 "logtags.gperf" {"ssh.debug", 111}, #line 96 "logtags.gperf" {"rsh.policy", 90}, {"",0}, {"",0}, {"",0}, #line 132 "logtags.gperf" {"tftp.violation", 126}, #line 90 "logtags.gperf" {"rdp.policy", 84}, {"",0}, #line 111 "logtags.gperf" {"smtp.request", 105}, {"",0}, {"",0}, #line 108 "logtags.gperf" {"smtp.error", 102}, #line 116 "logtags.gperf" {"sqlnet.violation", 110}, {"",0}, #line 112 "logtags.gperf" {"smtp.response", 106}, #line 134 "logtags.gperf" {"vnc.error", 128}, #line 65 "logtags.gperf" {"nntp.reply", 59}, #line 110 "logtags.gperf" {"smtp.policy", 104}, #line 66 "logtags.gperf" {"nntp.request", 60}, #line 89 "logtags.gperf" {"rdp.info", 83}, #line 77 "logtags.gperf" {"pop3.violation", 71}, #line 67 "logtags.gperf" {"nntp.trace", 61}, {"",0}, {"",0}, {"",0}, #line 133 "logtags.gperf" {"vnc.debug", 127}, #line 119 "logtags.gperf" {"ssh.policy", 113}, #line 64 "logtags.gperf" {"nntp.policy", 58}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 104 "logtags.gperf" {"sip.error", 98}, {"",0}, #line 137 "logtags.gperf" {"vnc.session", 131}, {"",0}, #line 122 "logtags.gperf" {"ssh.info", 116}, #line 113 "logtags.gperf" {"smtp.violation", 107}, #line 41 "logtags.gperf" {"imap.reply", 35}, #line 100 "logtags.gperf" {"sip.request", 94}, #line 42 "logtags.gperf" {"imap.request", 36}, #line 97 "logtags.gperf" {"rsh.violation", 91}, #line 103 "logtags.gperf" {"sip.debug", 97}, #line 38 "logtags.gperf" {"imap.error", 32}, {"",0}, #line 101 "logtags.gperf" {"sip.response", 95}, #line 92 "logtags.gperf" {"rdp.violation", 86}, {"",0}, #line 136 "logtags.gperf" {"vnc.policy", 130}, #line 40 "logtags.gperf" {"imap.policy", 34}, {"",0}, {"",0}, #line 93 "logtags.gperf" {"rsh.accounting", 87}, {"",0}, {"",0}, #line 124 "logtags.gperf" {"telnet.error", 118}, #line 125 "logtags.gperf" {"telnet.policy", 119}, {"",0}, #line 11 "logtags.gperf" {"core.error", 5}, {"",0}, #line 123 "logtags.gperf" {"telnet.debug", 117}, #line 135 "logtags.gperf" {"vnc.info", 129}, {"",0}, {"",0}, #line 15 "logtags.gperf" {"core.policy", 9}, {"",0}, #line 120 "logtags.gperf" {"ssh.violation", 114}, {"",0}, #line 102 "logtags.gperf" {"sip.policy", 96}, {"",0}, {"",0}, {"",0}, #line 43 "logtags.gperf" {"imap.violation", 37}, #line 128 "logtags.gperf" {"tftp.debug", 122}, #line 126 "logtags.gperf" {"telnet.violation", 120}, {"",0}, {"",0}, #line 121 "logtags.gperf" {"ssh.accounting", 115}, #line 78 "logtags.gperf" {"pssl.debug", 72}, #line 17 "logtags.gperf" {"core.stderr", 11}, #line 47 "logtags.gperf" {"ldap.request", 41}, {"",0}, {"",0}, #line 45 "logtags.gperf" {"ldap.error", 39}, {"",0}, #line 127 "logtags.gperf" {"telnet.violations", 121}, #line 48 "logtags.gperf" {"ldap.response", 42}, {"",0}, #line 72 "logtags.gperf" {"pop3.debug", 66}, #line 46 "logtags.gperf" {"ldap.policy", 40}, {"",0}, #line 138 "logtags.gperf" {"vnc.violation", 132}, {"",0}, #line 69 "logtags.gperf" {"plug.error", 63}, {"",0}, #line 16 "logtags.gperf" {"core.session", 10}, {"",0}, #line 109 "logtags.gperf" {"smtp.info", 103}, #line 106 "logtags.gperf" {"smtp.accounting", 100}, #line 70 "logtags.gperf" {"plug.policy", 64}, #line 19 "logtags.gperf" {"finger.error", 13}, #line 20 "logtags.gperf" {"finger.policy", 14}, #line 21 "logtags.gperf" {"finger.request", 15}, {"",0}, #line 140 "logtags.gperf" {"whois.error", 134}, #line 18 "logtags.gperf" {"finger.debug", 12}, #line 141 "logtags.gperf" {"whois.request", 135}, {"",0}, #line 107 "logtags.gperf" {"smtp.debug", 101}, #line 139 "logtags.gperf" {"whois.debug", 133}, {"",0}, #line 99 "logtags.gperf" {"sip.violation", 93}, #line 49 "logtags.gperf" {"ldap.violation", 43}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 63 "logtags.gperf" {"nntp.debug", 57}, #line 22 "logtags.gperf" {"finger.violation", 16}, {"",0}, #line 51 "logtags.gperf" {"lp.error", 45}, #line 105 "logtags.gperf" {"sip.accounting", 99}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 54 "logtags.gperf" {"mime.error", 48}, {"",0}, #line 71 "logtags.gperf" {"plug.session", 65}, {"",0}, #line 39 "logtags.gperf" {"imap.info", 33}, {"",0}, #line 55 "logtags.gperf" {"mime.policy", 49}, #line 82 "logtags.gperf" {"radius.error", 76}, #line 83 "logtags.gperf" {"radius.policy", 77}, #line 84 "logtags.gperf" {"radius.request", 78}, {"",0}, {"",0}, #line 81 "logtags.gperf" {"radius.debug", 75}, {"",0}, {"",0}, #line 37 "logtags.gperf" {"imap.debug", 31}, #line 58 "logtags.gperf" {"msrpc.error", 52}, #line 60 "logtags.gperf" {"msrpc.policy", 54}, {"",0}, #line 12 "logtags.gperf" {"core.info", 6}, #line 7 "logtags.gperf" {"core.accounting", 1}, #line 57 "logtags.gperf" {"msrpc.debug", 51}, #line 34 "logtags.gperf" {"http.request", 28}, {"",0}, #line 85 "logtags.gperf" {"radius.session", 79}, #line 32 "logtags.gperf" {"http.error", 26}, #line 86 "logtags.gperf" {"radius.violation", 80}, {"",0}, #line 35 "logtags.gperf" {"http.response", 29}, #line 56 "logtags.gperf" {"mime.violation", 50}, #line 9 "logtags.gperf" {"core.debug", 3}, #line 33 "logtags.gperf" {"http.policy", 27}, {"",0}, #line 61 "logtags.gperf" {"msrpc.session", 55}, #line 10 "logtags.gperf" {"core.dump", 4}, #line 62 "logtags.gperf" {"msrpc.violation", 56}, {"",0}, {"",0}, {"",0}, #line 24 "logtags.gperf" {"ftp.error", 18}, {"",0}, {"",0}, #line 13 "logtags.gperf" {"core.license", 7}, {"",0}, #line 26 "logtags.gperf" {"ftp.reply", 20}, #line 53 "logtags.gperf" {"lp.request", 47}, #line 27 "logtags.gperf" {"ftp.request", 21}, #line 14 "logtags.gperf" {"core.message", 8}, {"",0}, #line 23 "logtags.gperf" {"ftp.debug", 17}, #line 59 "logtags.gperf" {"msrpc.info", 53}, {"",0}, {"",0}, {"",0}, #line 36 "logtags.gperf" {"http.violation", 30}, #line 44 "logtags.gperf" {"ldap.debug", 38}, {"",0}, {"",0}, {"",0}, #line 143 "logtags.gperf" {"x11.error", 137}, {"",0}, #line 28 "logtags.gperf" {"ftp.session", 22}, {"",0}, {"",0}, {"",0}, #line 68 "logtags.gperf" {"plug.debug", 62}, {"",0}, {"",0}, {"",0}, #line 142 "logtags.gperf" {"x11.debug", 136}, {"",0}, #line 98 "logtags.gperf" {"satyr.error", 92}, {"",0}, {"",0}, {"",0}, #line 25 "logtags.gperf" {"ftp.policy", 19}, {"",0}, {"",0}, {"",0}, #line 8 "logtags.gperf" {"core.auth", 2}, {"",0}, #line 146 "logtags.gperf" {"x11.session", 140}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 145 "logtags.gperf" {"x11.policy", 139}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 52 "logtags.gperf" {"lp.policy", 46}, {"",0}, {"",0}, {"",0}, #line 144 "logtags.gperf" {"x11.info", 138}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 30 "logtags.gperf" {"http.accounting", 24}, {"",0}, {"",0}, #line 29 "logtags.gperf" {"ftp.violation", 23}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 31 "logtags.gperf" {"http.debug", 25}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, {"",0}, #line 147 "logtags.gperf" {"x11.violation", 141}, {"",0}, {"",0}, {"",0}, {"",0}, #line 50 "logtags.gperf" {"lp.debug", 44} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) { register int key = hash (str, len); if (key <= MAX_HASH_VALUE && key >= 0) { register const char *s = wordlist[key].name; if (*str == *s && !strcmp (str + 1, s + 1)) return &wordlist[key]; } } return 0; } zorp-3.9.5/zorp/logtags_gperf.h000066400000000000000000000072671172670260400165310ustar00rootroot00000000000000/* automatically generated by genlutable.sh, do not edit directly */ #ifndef LOGTAG_NAMES_DEFINED #define LOGTAG_NAMES_DEFINED enum { LOGTAG_CORE_ACCOUNTING=1, LOGTAG_CORE_AUTH=2, LOGTAG_CORE_DEBUG=3, LOGTAG_CORE_DUMP=4, LOGTAG_CORE_ERROR=5, LOGTAG_CORE_INFO=6, LOGTAG_CORE_LICENSE=7, LOGTAG_CORE_MESSAGE=8, LOGTAG_CORE_POLICY=9, LOGTAG_CORE_SESSION=10, LOGTAG_CORE_STDERR=11, LOGTAG_FINGER_DEBUG=12, LOGTAG_FINGER_ERROR=13, LOGTAG_FINGER_POLICY=14, LOGTAG_FINGER_REQUEST=15, LOGTAG_FINGER_VIOLATION=16, LOGTAG_FTP_DEBUG=17, LOGTAG_FTP_ERROR=18, LOGTAG_FTP_POLICY=19, LOGTAG_FTP_REPLY=20, LOGTAG_FTP_REQUEST=21, LOGTAG_FTP_SESSION=22, LOGTAG_FTP_VIOLATION=23, LOGTAG_HTTP_ACCOUNTING=24, LOGTAG_HTTP_DEBUG=25, LOGTAG_HTTP_ERROR=26, LOGTAG_HTTP_POLICY=27, LOGTAG_HTTP_REQUEST=28, LOGTAG_HTTP_RESPONSE=29, LOGTAG_HTTP_VIOLATION=30, LOGTAG_IMAP_DEBUG=31, LOGTAG_IMAP_ERROR=32, LOGTAG_IMAP_INFO=33, LOGTAG_IMAP_POLICY=34, LOGTAG_IMAP_REPLY=35, LOGTAG_IMAP_REQUEST=36, LOGTAG_IMAP_VIOLATION=37, LOGTAG_LDAP_DEBUG=38, LOGTAG_LDAP_ERROR=39, LOGTAG_LDAP_POLICY=40, LOGTAG_LDAP_REQUEST=41, LOGTAG_LDAP_RESPONSE=42, LOGTAG_LDAP_VIOLATION=43, LOGTAG_LP_DEBUG=44, LOGTAG_LP_ERROR=45, LOGTAG_LP_POLICY=46, LOGTAG_LP_REQUEST=47, LOGTAG_MIME_ERROR=48, LOGTAG_MIME_POLICY=49, LOGTAG_MIME_VIOLATION=50, LOGTAG_MSRPC_DEBUG=51, LOGTAG_MSRPC_ERROR=52, LOGTAG_MSRPC_INFO=53, LOGTAG_MSRPC_POLICY=54, LOGTAG_MSRPC_SESSION=55, LOGTAG_MSRPC_VIOLATION=56, LOGTAG_NNTP_DEBUG=57, LOGTAG_NNTP_POLICY=58, LOGTAG_NNTP_REPLY=59, LOGTAG_NNTP_REQUEST=60, LOGTAG_NNTP_TRACE=61, LOGTAG_PLUG_DEBUG=62, LOGTAG_PLUG_ERROR=63, LOGTAG_PLUG_POLICY=64, LOGTAG_PLUG_SESSION=65, LOGTAG_POP3_DEBUG=66, LOGTAG_POP3_ERROR=67, LOGTAG_POP3_POLICY=68, LOGTAG_POP3_REPLY=69, LOGTAG_POP3_REQUEST=70, LOGTAG_POP3_VIOLATION=71, LOGTAG_PSSL_DEBUG=72, LOGTAG_PSSL_ERROR=73, LOGTAG_PSSL_POLICY=74, LOGTAG_RADIUS_DEBUG=75, LOGTAG_RADIUS_ERROR=76, LOGTAG_RADIUS_POLICY=77, LOGTAG_RADIUS_REQUEST=78, LOGTAG_RADIUS_SESSION=79, LOGTAG_RADIUS_VIOLATION=80, LOGTAG_RDP_DEBUG=81, LOGTAG_RDP_ERROR=82, LOGTAG_RDP_INFO=83, LOGTAG_RDP_POLICY=84, LOGTAG_RDP_SESSION=85, LOGTAG_RDP_VIOLATION=86, LOGTAG_RSH_ACCOUNTING=87, LOGTAG_RSH_DEBUG=88, LOGTAG_RSH_ERROR=89, LOGTAG_RSH_POLICY=90, LOGTAG_RSH_VIOLATION=91, LOGTAG_SATYR_ERROR=92, LOGTAG_SIP_VIOLATION=93, LOGTAG_SIP_REQUEST=94, LOGTAG_SIP_RESPONSE=95, LOGTAG_SIP_POLICY=96, LOGTAG_SIP_DEBUG=97, LOGTAG_SIP_ERROR=98, LOGTAG_SIP_ACCOUNTING=99, LOGTAG_SMTP_ACCOUNTING=100, LOGTAG_SMTP_DEBUG=101, LOGTAG_SMTP_ERROR=102, LOGTAG_SMTP_INFO=103, LOGTAG_SMTP_POLICY=104, LOGTAG_SMTP_REQUEST=105, LOGTAG_SMTP_RESPONSE=106, LOGTAG_SMTP_VIOLATION=107, LOGTAG_SQLNET_ERROR=108, LOGTAG_SQLNET_REQUEST=109, LOGTAG_SQLNET_VIOLATION=110, LOGTAG_SSH_DEBUG=111, LOGTAG_SSH_ERROR=112, LOGTAG_SSH_POLICY=113, LOGTAG_SSH_VIOLATION=114, LOGTAG_SSH_ACCOUNTING=115, LOGTAG_SSH_INFO=116, LOGTAG_TELNET_DEBUG=117, LOGTAG_TELNET_ERROR=118, LOGTAG_TELNET_POLICY=119, LOGTAG_TELNET_VIOLATION=120, LOGTAG_TELNET_VIOLATIONS=121, LOGTAG_TFTP_DEBUG=122, LOGTAG_TFTP_ERROR=123, LOGTAG_TFTP_POLICY=124, LOGTAG_TFTP_REQUEST=125, LOGTAG_TFTP_VIOLATION=126, LOGTAG_VNC_DEBUG=127, LOGTAG_VNC_ERROR=128, LOGTAG_VNC_INFO=129, LOGTAG_VNC_POLICY=130, LOGTAG_VNC_SESSION=131, LOGTAG_VNC_VIOLATION=132, LOGTAG_WHOIS_DEBUG=133, LOGTAG_WHOIS_ERROR=134, LOGTAG_WHOIS_REQUEST=135, LOGTAG_X11_DEBUG=136, LOGTAG_X11_ERROR=137, LOGTAG_X11_INFO=138, LOGTAG_X11_POLICY=139, LOGTAG_X11_SESSION=140, LOGTAG_X11_VIOLATION=141, LOGTAG_MAX=142 }; #endif zorp-3.9.5/zorp/main.c000066400000000000000000000315351172670260400146200ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary * All rights reserved. * * Author : bazsi * Auditor : kisza * Last version : 1.24 * Notes : * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_DMALLOC #include #endif #if HAVE_SYS_PRCTL_H #include #endif #include #include "logtags_gperf.c" static gint z_logtag_lookup(const gchar *tag, gsize len) { const struct tagid *p = NULL; p = z_logtag_lookup_gperf(tag, len); if (G_LIKELY(p)) return p->id; else return -1; } void z_sigterm_handler(int signo G_GNUC_UNUSED) { z_main_loop_initiate_termination(TRUE); } void z_ignore_signal_handler(int signo G_GNUC_UNUSED) { } void z_sighup_handler(int signo G_GNUC_UNUSED) { z_main_loop_initiate_reload(TRUE); } void z_sigchild_handler(int signo G_GNUC_UNUSED) { while (waitpid(-1, NULL, WNOHANG) > 0) ; } void z_fatal_signal_handler(int signo) { ZSignalContext *p = z_stackdump_get_context(p); struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = SIG_DFL; sigaction(signo, &act, NULL); /*LOG This message is logged when Zorp caught a fatal signal. Possible reason is bad RAM or other hardware. */ z_log(NULL, CORE_ERROR, 0, "Signal received, stackdump follows; signo='%d'", signo); z_mem_trace_stats(); z_stackdump_log(p); kill(getpid(), signo); } void z_sigusr1_handler(int signo G_GNUC_UNUSED) { usr1_received = 1; } void z_sigusr2_handler(int signo G_GNUC_UNUSED) { usr2_received = 1; } void z_setup_signals(void) { struct sigaction act; sigset_t ss; sigemptyset(&ss); memset(&act, 0, sizeof(act)); act.sa_handler = z_ignore_signal_handler; sigaction(SIGPIPE, &act, NULL); sigaddset(&ss, SIGPIPE); memset(&act, 0, sizeof(act)); act.sa_handler = z_sigterm_handler; sigaction(SIGTERM, &act, NULL); sigaddset(&ss, SIGTERM); sigaction(SIGINT, &act, NULL); sigaddset(&ss, SIGINT); memset(&act, 0, sizeof(act)); act.sa_handler = z_fatal_signal_handler; sigaction(SIGSEGV, &act, NULL); sigaddset(&ss, SIGSEGV); sigaction(SIGABRT, &act, NULL); sigaddset(&ss, SIGABRT); sigaction(SIGILL, &act, NULL); sigaddset(&ss, SIGILL); memset(&act, 0, sizeof(act)); act.sa_handler = z_ignore_signal_handler; sigaction(SIGTRAP, &act, NULL); sigaddset(&ss, SIGTRAP); memset(&act, 0, sizeof(act)); act.sa_handler = z_sigchild_handler; sigaction(SIGCHLD, &act, NULL); sigaddset(&ss, SIGCHLD); memset(&act, 0, sizeof(act)); act.sa_handler = z_sighup_handler; sigaction(SIGHUP, &act, NULL); sigaddset(&ss, SIGHUP); memset(&act, 0, sizeof(act)); act.sa_handler = z_sigusr1_handler; sigaction(SIGUSR1, &act, NULL); sigaddset(&ss, SIGUSR1); memset(&act, 0, sizeof(act)); act.sa_handler = z_sigusr2_handler; sigaction(SIGUSR2, &act, NULL); sigaddset(&ss, SIGUSR2); sigprocmask(SIG_UNBLOCK, &ss, NULL); } #define ON_OFF_STR(x) (x ? "on" : "off") void z_version(void) { printf("Zorp %s (%s)\n" "Revision: %s\n" "Compile-Date: %s %s\n" "Config-Date: %s\n" "Trace: %s\n" "Debug: %s\n" "IPOptions: %s\n" "%s\n" , BROCHURE_VERSION, VERSION, ZORP_SOURCE_REVISION, __DATE__, __TIME__, ZORP_CONFIG_DATE, ON_OFF_STR(ENABLE_TRACE), ON_OFF_STR(ENABLE_DEBUG), ON_OFF_STR(ENABLE_IPOPTIONS), z_libzorpll_version_info() ); } /* arguments */ #define MAX_SOFT_INSTANCES 128 static const gchar *instance_policy_list[MAX_SOFT_INSTANCES + 1]; static gint instance_count = 1; static const gchar *policy_file = ZORP_POLICY_FILE; static gboolean log_escape = FALSE; static gboolean display_version = FALSE; static gboolean z_set_instance_name(const gchar *option_name G_GNUC_UNUSED, const gchar *value, gpointer user_datae G_GNUC_UNUSED, GError **error G_GNUC_UNUSED ) { instance_name = g_strdup(value); instance_policy_list[0] = (gchar *) instance_name; instance_count = 1; return TRUE; } static gboolean z_set_virtual_instance_name(const char *option_name, const gchar *value, gpointer user_data G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) { if (strcmp(option_name, "--slave") == 0) zorp_process_master_mode = FALSE; virtual_instance_name = g_strdup(value); return TRUE; } static gint deadlock_checker_timeout = DEADLOCK_CHECKER_DEFAULT_TIMEOUT; static GOptionEntry zorp_options[] = { { "as", 'a', 0, G_OPTION_ARG_CALLBACK, z_set_instance_name, "Set instance name", "" }, { "master", 0, 0, G_OPTION_ARG_CALLBACK, z_set_virtual_instance_name, "Run in master mode with the virtual instance name specified", ""}, { "slave", 0, 0, G_OPTION_ARG_CALLBACK, z_set_virtual_instance_name, "Run in slave mode with the virtual instance name specified", ""}, { "policy", 'p', 0, G_OPTION_ARG_STRING, &policy_file, "Set policy file", "" }, { "version", 'V', 0, G_OPTION_ARG_NONE, &display_version, "Display version number", NULL }, { "log-escape", 0, 0, G_OPTION_ARG_NONE, &log_escape, "Escape log messages to avoid non-printable characters", NULL }, { "deadlock-check-timeout", 0, 0, G_OPTION_ARG_INT, &deadlock_checker_timeout, "Timeout for deadlock detection queries in seconds", NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; static gboolean zorp_deadlock_checker(void) { struct sockaddr_un unaddr; gint fd = -1, len; gboolean res = FALSE; const gchar *request = "GETVALUE zorp.info.policy.file_stamp\n"; gchar response[1024]; struct timeval tv; fd_set rdset; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) { z_process_message("Cannot create socket; reason='%s'\n", strerror(errno)); goto finish; } unaddr.sun_family = AF_UNIX; snprintf(unaddr.sun_path, sizeof(unaddr.sun_path), "%s.%s", ZORP_SZIG_SOCKET_NAME, virtual_instance_name); if (connect(fd, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) { z_process_message("Cannot connect to SZIG socket; socket='%s', reason='%s'\n", unaddr.sun_path, strerror(errno)); goto finish; } if (write(fd, request, strlen(request)) < 0) { z_process_message("Error sending request to SZIG socket; reason='%s'\n", strerror(errno)); goto finish; } tv.tv_sec = deadlock_checker_timeout; tv.tv_usec = 0; FD_ZERO(&rdset); FD_SET(fd, &rdset); len = select(fd + 1, &rdset, NULL, NULL, &tv); switch (len) { case 0: /* ok, but no data available within the timeout */ z_process_message("Timeout expired while reading SZIG response;\n"); goto finish; case 1: /* ok, data is available for reading */ break; case -1: /* error, reason comes in errno */ z_process_message("Error reading SZIG response; reason='%s'\n", strerror(errno)); goto finish; default: /* this just can't happen */ g_assert_not_reached(); break; } if ((len = read(fd, response, sizeof(response) - 1)) < 0) { z_process_message("Error reading SZIG response; reason='%s'\n", strerror(errno)); goto finish; } response[len] = 0; if (response[len - 1] == '\n') response[len - 1] = 0; res = TRUE; finish: if (fd >= 0) close(fd); return res; } int main(int argc, char *argv[]) { gchar log_progname[128]; const gchar *pid_file = NULL; gchar pid_file_buf[128]; GOptionContext *ctx; gboolean foreground = FALSE; GError *error = NULL; z_mem_trace_init("zorp-memtrace.txt"); instance_name = "zorp"; instance_policy_list[0] = "zorp"; virtual_instance_name = NULL; z_log_set_defaults(3, TRUE, TRUE, ""); z_thread_set_max_threads(1000); /* set our own default value for max_threads in ZThread */ z_process_set_argv_space((gint) argc, (gchar **) argv); z_process_set_caps("cap_net_admin,cap_net_bind_service,cap_net_raw=p"); ctx = g_option_context_new("zorp"); z_libzorpll_add_option_groups(ctx, 0); g_option_context_add_main_entries(ctx, zorp_options, NULL); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { fprintf(stderr, "%s: %s", instance_name, error ? error->message : "Unknown error"); exit(1); } g_option_context_free(ctx); if (argc > 1) { fprintf(stderr, "%s: Invalid arguments.\n", instance_name); return 1; } instance_policy_list[instance_count] = NULL; if (!virtual_instance_name) virtual_instance_name = g_strdup(instance_name); if (display_version) { z_version(); exit(0); } if (!z_log_get_use_syslog()) foreground = TRUE; if (pid_file == NULL) { g_snprintf(pid_file_buf, sizeof(pid_file_buf), "zorp-%s.pid", virtual_instance_name); pid_file = pid_file_buf; } /* NOTE: these do not override the values set by the user using command line arguments */ z_process_set_pidfile_dir(ZORP_PID_FILE_DIR); z_process_set_working_dir(ZORP_WORKING_DIR); z_process_set_pidfile(pid_file); z_process_set_name(virtual_instance_name); z_process_set_use_fdlimit(TRUE); z_process_set_check(deadlock_checker_timeout, zorp_deadlock_checker); /* NOTE: the current user is root and there is no user/group specified, * then assume 'zorp'/'zorp' */ if (getuid() == 0) { z_process_set_user("zorp"); z_process_set_group("zorp"); } if (foreground) z_process_set_mode(Z_PM_FOREGROUND); /* NOTE: if startup fails, z_process_start() prints an appropriate * error to stderr and exits the process */ z_process_start(); startup_id = time(NULL); /* NOTE: this is the daemon process, we have stderr on the first * invocation, e.g. when we are not restarted automatically, the user * probably sees messages printed to stderr. */ z_thread_init(); g_main_context_acquire(NULL); g_snprintf(log_progname, sizeof(log_progname), "zorp/%s", instance_name); /*NOLOG*/ if (!z_log_init(log_progname, ZLF_THREAD | (log_escape ? ZLF_ESCAPE : 0))) { fprintf(stderr, "%s: Error initializing logging subsystem\n", instance_name); exit_code = 1; goto deinit_exit; } z_log_enable_tag_map_cache(z_logtag_lookup, TOTAL_KEYWORDS); /*LOG This message reports the current verbosity level of Zorp. */ z_log(NULL, CORE_DEBUG, 0, "Starting up; verbose_level='%d', version='%s (%s)', startup_id='%d'", z_log_get_verbose_level(), BROCHURE_VERSION, VERSION, startup_id); z_dgram_init(); z_tp_socket_init(); z_ssl_init(); z_szig_init(virtual_instance_name); z_main_loop_init(); z_ifmon_init(); z_dispatch_init(); z_registry_init(); z_proxy_hash_init(); /* only used for PORT allocation within a given range */ srand(time(NULL) ^ getpid()); if (!z_python_init()) { /*LOG This message indicates that Zorp was unable to initialize the Python engine. It is likely that your installation is broken. Check your packages and there version number. */ z_llog(CORE_ERROR, 0, "Error initializing Python policy engine;"); fprintf(stderr, "%s: Error initializing Python policy engine\n", instance_name); exit_code = 1; goto deinit_exit; } z_setup_signals(); /*NOLOG*/ z_main_loop(policy_file, instance_name, instance_policy_list, virtual_instance_name, zorp_process_master_mode); deinit_exit: /*NOLOG*/ z_llog(CORE_INFO, 3, "Shutting down; version='%s (%s)'", BROCHURE_VERSION, VERSION); z_thread_destroy(); z_python_destroy(); z_dispatch_destroy(); z_ifmon_destroy(); z_main_loop_destroy(); z_ssl_destroy(); z_log_destroy(); z_proxy_hash_destroy(); z_mem_trace_dump(); #ifdef USE_DMALLOC dmalloc_shutdown(); /* avoid second dump of dmalloc */ rename("logfile", "logfile.dm"); #endif if (exit_code != 0) z_process_startup_failed(exit_code, TRUE); z_process_finish(); return exit_code; } zorp-3.9.5/zorp/policy.py.sample000066400000000000000000000105321172670260400166530ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary ## All rights reserved. ## ############################################################################ # # sample firewall policy with transparent access to FTP, HTTP and CVS protocols. # For FTP and HTTP we use application level gateways, for CVS we use a plug. # (as long as CVS protocol proxy is not available) # # firewall internal network: 192.168.1.0/24 # firewall internal interface: 192.168.1.1 # firewall external interface: 193.225.235.6 # from Zorp.Core import * from Zorp.Plug import * from Zorp.Http import * from Zorp.Ftp import * Zorp.firewall_name = 'zorp@site' InetZone("site-net", "192.168.1.0/24", # list of allowed outbound services, '*' matches anything outbound_services=["intra_http", "intra_ftp", "intra_cvs"], # list of allowed inbound services, '*' matches anything inbound_services=[]) InetZone("local", "127.0.0.0/8", inbound_services=["*"], outbound_services=[]) InetZone("internet", "0.0.0.0/0", inbound_services=["*"], outbound_services=[]) # # Here's a proxy event handler definition. We are deriving from a # simple plug proxy, which is blindly copying in both directions. # # Instances of this class represent a "plug proxy". For a complete # documentation for the features and available attributes of plug see the # file /doc/modules/plug.txt # class IntraCvs(PlugProxy): def config(self): """ The config event is sent in configuration state, some attributes can only be set here. """ # uncommenting this would make this plug one-way only (server->client) #self.copy_to_server = FALSE # same but client->server copying would only be performed #self.copy_to_client = FALSE self.packet_stats_interval = 100 def startUp(self): """ startUp is called after configuration, but before any data is transferred. """ # this is empty now pass def shutDown(self): """ called just before terminating the proxy. """ pass def packetStats(self, client_bytes, client_pkt, server_bytes, server_pkt): """ plug is sending this event after self.packet_stats_interval number of packets had been transferred. """ # report traffic information proxyLog(self, 'plug.debug', 3, "server->client: packet=%d, bytes=%d, bandwidth=%f" % (client_pkt, client_bytes, self.bandwidth_to_client)) proxyLog(self, 'plug.debug', 3, "client->server: packet=%d, bytes=%d, bandwidth=%f" % (server_pkt, server_bytes, self.bandwidth_to_server)) return 1 # # Let's define a transparent http proxy, which rewrites the user_agent # header to something different. # class IntraHttp(HttpProxy): def config(self): HttpProxy.config(self) self.transparent_mode = TRUE self.request_headers["User-Agent"] = (HTTP_HDR_CHANGE_VALUE, "Lynx/2.8.3rel.1") self.request["GET"] = (HTTP_REQ_POLICY, self.filterURL) # self.parent_proxy = "proxy.site.net" # self.parent_proxy_port = 3128 # self.timeout = 60000 # self.max_keepalive_requests = 10 def filterURL(self, method, url, version): # return HTTP_REQ_REJECT here to reject this request # change self.request_url to redirect to another url # change connection_mode to HTTP_CONNECTION_CLOSE to force kept-alive connections to close log("http.info", 3, "%s: GET: %s" % (self.session.session_id, url)) class IntraFtp(FtpProxy): def config(self): FtpProxy.config(self) # # The name of this function is passed to the Zorp binary with the --as # command line option. # # zorp_http instance def zorp_http(): # create services Service("intra_http", IntraHttp) Service("intra_ftp", IntraFtp) # bind services to listeners # you'll need the packet filter redirect these connections, and # to protect transparent listeners, since if you connect to # a transparent listener directly, Zorp reconnects to itself. Listener(SockAddrInet("192.168.1.1", 50080), "intra_http") Listener(SockAddrInet("192.168.1.1", 50021), "intra_ftp") # zorp_plug instance def zorp_plug(): Service("intra_cvs", IntraCvs) Listener(SockAddrInet("192.168.1.1", 52401), "intra_cvs") zorp-3.9.5/zorp/urlfilter/000077500000000000000000000000001172670260400155315ustar00rootroot00000000000000zorp-3.9.5/zorp/urlfilter/Makefile.am000066400000000000000000000000241172670260400175610ustar00rootroot00000000000000SUBDIRS = whitelist zorp-3.9.5/zorp/urlfilter/whitelist/000077500000000000000000000000001172670260400175455ustar00rootroot00000000000000zorp-3.9.5/zorp/urlfilter/whitelist/Makefile.am000066400000000000000000000003221172670260400215760ustar00rootroot00000000000000EXTRA_DIST = urls domains install-exec-local: install -m 0755 -d $(DESTDIR)/$(sysconfdir)/urlfilter/whitelist install -m 0644 -t $(DESTDIR)/$(sysconfdir)/urlfilter/whitelist $(srcdir)/urls $(srcdir)/domains zorp-3.9.5/zorp/urlfilter/whitelist/domains000066400000000000000000000002471172670260400211250ustar00rootroot00000000000000# This is an example URL filter file defining the list of whitelisted domains. # Add your own domains to this file (one domain per line) to add them to the whitelist. zorp-3.9.5/zorp/urlfilter/whitelist/urls000066400000000000000000000002361172670260400204560ustar00rootroot00000000000000# This is an example URL filter file defining the list of whitelisted URLs. # Add your own URLs to this file (one URL per line) to add them to the whitelist. zorp-3.9.5/zorpctl/000077500000000000000000000000001172670260400142245ustar00rootroot00000000000000zorp-3.9.5/zorpctl/Makefile.am000066400000000000000000000002771172670260400162660ustar00rootroot00000000000000LIBS=@SOCK_LIBS@ @GLIB_LIBS@ sbin_PROGRAMS = zorpctl zorpctl_SOURCES = main.c szig.c zorpctl.h szig.h sysconf_DATA = instances.conf.sample zorpctl.conf.sample EXTRA_DIST = $(sysconf_DATA) zorp-3.9.5/zorpctl/instances.conf.sample000066400000000000000000000010431172670260400203400ustar00rootroot00000000000000############################################################################ ## ## Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary ## All rights reserved. ## ############################################################################ # # This file lists the Zorp instances you want to run. # # The instance name and arguments _must_ be separated by spaces instead # of tabs! Otherwise zorpctl will stop working. #instance arguments #zorp_http --verbose=5 --policy /etc/zorp/policy-http.py #zorp_plug --policy /etc/zorp/policy-plug.py zorp-3.9.5/zorpctl/main.c000066400000000000000000001736361172670260400153340ustar00rootroot00000000000000/*************************************************************************** * * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * 2010, 2011 BalaBit IT Ltd, Budapest, Hungary * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Note that this permission is granted for only version 2 of the GPL. * * As an additional exemption you are allowed to compile & link against the * OpenSSL libraries as published by the OpenSSL project. See the file * COPYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author : bazsi * Auditor : * Last version : * Notes : * ***************************************************************************/ #include "zorpctl.h" #include "szig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ZORP ZORP_LIBDIR "/zorp" #if 1 #define ZORP_INSTANCES_CONF ZORP_SYSCONFDIR "/instances.conf" #define ZORP_ZORPCTL_CONF ZORP_SYSCONFDIR "/zorpctl.conf" #else #define ZORP_INSTANCES_CONF "instances.conf" #define ZORP_ZORPCTL_CONF "zorpctl.conf" #endif #define UNUSED __attribute__((unused)) #define MAX_ZORPCTL_LINE_LENGTH 4096 #ifndef MAX # define MAX(a,b) ((a) < (b) ? (b) : (a)) #endif /* Refer a string or the constant "(null)" if it's NULL * This is the default behaviour on Linux when printf()ing NULL, but on Solaris * it triggers a SIGSEGV */ #define SAFE_STR(n) ((n) ? (n) : "(null)") #define SAFE_STR_EMPTY(n) ((n) ? (n) : "") /* Currently the only flag for z_process_status_instance() */ #define ZORPCTL_STATUS_VERBOSE 1 typedef struct _ZInstance ZInstance; enum ZVirtInstanceRole { NONE, MASTER, SLAVE }; struct _ZInstance { ZInstance *next; char *real_name, *virtual_name; char **zorp_argv; int zorp_argc; char **zorpctl_argv; int zorpctl_argc; int auto_restart; int process_limit; int enable_core; int no_auto_start; int parallel_instances; enum ZVirtInstanceRole role; }; typedef struct _ZCommand ZCommand; struct _ZCommand { char *cmd; int (*func)(int argc, char *argv[]); char *help; }; typedef int (*ZCmdFunc)(ZInstance *inst, void *user_data); ZInstance *instances; extern ZCommand commands[]; typedef struct { int pid; /* %d The process id.*/ char comm[4096]; /* %s The filename of the executable, in parentheses. */ char state; /* %c R=running, S=sleeping, D=disk sleep, Z=zombie, T=traced/stopped, W=paging. */ int ppid; /* %d The PID of the parent. */ int pgrp; /* %d The process group ID of the process. */ int session; /* %d The session ID of the process. */ int tty_nr; /* %d The tty the process uses. */ int tpgid; /* %d The process group ID of the process which currently owns the tty that the process is connected to. */ unsigned long flags; /* %lu The kernel flags word of the process. For bit meanings, see the PF_* defines in . */ unsigned long minflt; /* %lu The number of minor faults the process has made which have not required loading a memory page from disk. */ unsigned long cminflt; /* %lu The number of minor faults that the process's waited-for children have made. */ unsigned long majflt; /* %lu The number of major faults the process has made which have required loading a memory page from disk. */ unsigned long cmajflt; /* %lu The number of major faults that the process's waited-for children have made. */ unsigned long utime; /* %lu The number of jiffies that this process has been scheduled in user mode. */ unsigned long stime; /* %lu The number of jiffies that this process has been scheduled in kernel mode. */ long cutime; /* %ld The number of jiffies that this process's waited-for children have been scheduled in user mode. (See also times(2).) */ long cstime; /* %ld The number of jiffies that this process's waited-for children have been scheduled in kernel mode. */ long priority; /* %ld The standard nice value, plus fifteen. The value is never negative in the kernel. */ long nice; /* %ld The nice value ranges from 19 (nicest) to -19 (not nice to others). */ long _dummyzero; /* %ld This value is hard coded to 0 as a placeholder for a removed field. */ long itrealvalue; /* %ld The time in jiffies before the next SIGALRM is sent to the process due to an interval timer. */ unsigned long starttime; /* %lu The time in jiffies the process started after system boot. */ unsigned long vsize; /* %lu Virtual memory size in bytes. */ long rss; /* %ld Resident Set Size: number of pages the process has in real memory, minus 3 for administrative purposes. */ unsigned long rlim; /* %lu Current limit in bytes on the rss of the process (usually 4294967295 on i386). */ unsigned long startcode; /* %lu The address above which program text can run. */ unsigned long endcode; /* %lu The address below which program text can run. */ unsigned long startstack; /* %lu The address of the start of the stack. */ unsigned long kstkesp; /* %lu The current value of esp (stack pointer), as found in the kernel stack page for the process. */ unsigned long kstkeip; /* %lu The current EIP (instruction pointer). */ unsigned long signal; /* %lu The bitmap of pending signals (usually 0). */ unsigned long blocked; /* %lu The bitmap of blocked signals (usually 0, 2 for shells). */ unsigned long sigignore; /* %lu The bitmap of ignored signals. */ unsigned long sigcatch; /* %lu The bitmap of catched signals. */ unsigned long wchan; /* %lu This is the "channel" in which the process is waiting. */ unsigned long nswap; /* %lu Number of pages swapped - not maintained. */ unsigned long cnswap; /* %lu Cumulative nswap for child processes. */ int exit_signal; /* %d Signal to be sent to parent when we die. */ int processor; /* %d CPU number last executed on. */ } ZProcInfo; /* Extended status info set by some z_process_ functions */ typedef enum _ZCtlCmdProcStatus { Z_CTL_CMD_PROC_STATUS_SUCCESS = 0, /* Command succeeded; can only be set when the result is success as well */ Z_CTL_CMD_PROC_STATUS_SUCCESS_WARN, /* Command succeeded; can be set when a command returns failure, but the end result may be considered successful */ Z_CTL_CMD_PROC_STATUS_GENERAL_FAILURE, /* Catch-all failure */ Z_CTL_CMD_PROC_STATUS_INVALID, /* Never returned by any command; can be used for handler which do not yet set the status */ } ZCtlCmdProcStatus; ZCtlCmdProcStatus z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_INVALID; int z_get_jiffies_per_sec(void) { static int jiffies_per_sec = -1; FILE *f; char buf[1024], *p; double idle_jiffies = 0, idle_sec = 0; int i; if (jiffies_per_sec > 0) return jiffies_per_sec; /* speedup by calculating only once */ f = fopen("/proc/stat", "r"); if (!f) return 0; while (fgets(buf, 1023, f)) { buf[1023] = '\0'; if (!strncmp("cpu ", buf, 4)) { p = buf; for (i = 0; *p && (i < 4); i++) { while (*p && (*p != ' ')) p++; /* skip non-whsp */ while (*p && (*p == ' ')) p++; /* skip whsp */ } if ((i != 4) || !*p) break; idle_jiffies = atof(p); break; } } fclose(f); if (idle_jiffies <= 0) return 0; f = fopen("/proc/uptime", "r"); if (!f || !fgets(buf, 1023, f)) return 0; buf[1023] = '\0'; p = buf; while (*p && (*p != ' ')) p++; /* skip non-whsp */ while (*p && (*p == ' ')) p++; /* skip whsp */ if (*p) idle_sec = atof(p); fclose(f); if (idle_sec <= 0) return 0; jiffies_per_sec = (int)((5 + (idle_jiffies / idle_sec)) / 10)*10; return jiffies_per_sec; } unsigned long z_get_uptime(void) { FILE *f; unsigned long i = 0; f = fopen("/proc/uptime", "r"); if (f) { /* Fancy call to fscanf to avoid unused return value warning. */ if (fscanf(f, "%lu", &i)) { }; fclose(f); } return i; } int z_get_proc_info(int pid, ZProcInfo *pi) { FILE *f; char filename[64]; int fieldnum; if ((pid <= 0) || !pi) return -1; sprintf(filename, "/proc/%d/stat", pid); f = fopen(filename, "r"); if (!f) return -2; fieldnum = fscanf(f, "%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu " "%ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu " "%lu %lu %lu %lu %lu %lu %lu %lu %d %d", &pi->pid, pi->comm, &pi->state, &pi->ppid, &pi->pgrp, &pi->session, &pi->tty_nr, &pi->tpgid, &pi->flags, &pi->minflt, &pi->cminflt, &pi->majflt, &pi->cmajflt, &pi->utime, &pi->stime, &pi->cutime, &pi->cstime, &pi->priority, &pi->nice, &pi->_dummyzero, &pi->itrealvalue, &pi->starttime, &pi->vsize, &pi->rss, &pi->rlim, &pi->startcode, &pi->endcode, &pi->startstack, &pi->kstkesp, &pi->kstkeip, &pi->signal, &pi->blocked, &pi->sigignore, &pi->sigcatch, &pi->wchan, &pi->nswap, &pi->cnswap, &pi->exit_signal, &pi->processor); fclose(f); return (fieldnum != 39); /* 0 on success */ } static int auto_restart = 1; static int start_wait_timeout = 10; static double stop_check_delay = 0.1; static int stop_check_timeout = 5; static int process_limit_reserve = 64; static int process_limit_threshold = -1; static int process_limit_min = 256; static int process_limit_sum = 0; static int check_perms = 1; static char *zorp_append_args = NULL; static char *zorpctl_append_args = NULL; static char *pidfile_dir = ZORP_PIDFILEDIR, *pidfile_dir_owner = NULL, *pidfile_dir_group = NULL, *pidfile_dir_perm = NULL; static char *config_dir = ZORP_SYSCONFDIR, *config_dir_owner = "root", *config_dir_group = "zorp", *config_dir_perm = "0750"; static char delayed_errors[16384]; static int sigalarm_received = 0; /* obsolete config variables */ static int start_check_timeout = -1; static int auto_restart_time_threshold = -1; /* obsolete */ static int auto_restart_max_count = -1; /* obsolete */ static double auto_restart_delay = -1.0; /* obsolete */ static inline void chomp(char *line) { int len = strlen(line); if (line[len-1] == '\n') line[len-1] = 0; } static inline char * strduplen(char *line, int len) { char *res; res = malloc(len + 1); strncpy(res, line, len+1); res[len] = 0; return res; } static void z_sigalarm_handler(int signo UNUSED) { sigalarm_received = 1; signal(SIGALRM, z_sigalarm_handler); } static inline void z_alarm_request(int seconds) { sigalarm_received = 0; alarm(seconds); } static inline int z_alarm_fired(void) { return sigalarm_received; } static inline void z_setup_signals(void) { siginterrupt(SIGALRM, 1); signal(SIGALRM, z_sigalarm_handler); } static void z_error(int delay, const char *format, ...) { char buf[1024]; va_list args; va_start(args, format); if (delay) { gsize len = vsnprintf(buf, sizeof(buf), format, args) + 1; strncat(delayed_errors, buf, sizeof(delayed_errors) - strlen(delayed_errors) - len - 1); } else { vfprintf(stderr, format, args); } va_end(args); } static void z_dump_errors(void) { if (delayed_errors[0]) { fprintf(stderr, "\nThe following errors occurred so far:\n%s\n", delayed_errors); delayed_errors[0] = 0; } } static int z_instance_running(ZInstance *inst, pid_t *pid, int *stale) { FILE *pidfile; char buf[256]; *stale = 0; snprintf(buf, sizeof(buf), "%s/zorp-%s.pid", pidfile_dir, inst->virtual_name); pidfile = fopen(buf, "r"); if (!pidfile) { return 0; } if (!fgets(buf, sizeof(buf), pidfile)) { fclose(pidfile); *stale = 1; return 0; } *pid = atoi(buf); fclose(pidfile); if (!(*pid)) { /* invalid pid, pid 0 */ *stale = 1; return 0; } if (kill(*pid, SIGCONT) == 0) { /* still running */ return 1; } /* stale pidfile */ *stale = 1; return 0; } static void z_instance_remove_stale_pidfile(const char *instance_name) { char buf[256]; snprintf(buf, sizeof(buf), "%s/zorp-%s.pid", pidfile_dir, instance_name); unlink(buf); } static void z_instance_free(ZInstance *inst) { free(inst->real_name); if (inst->zorp_argv) free(inst->zorp_argv); if (inst->zorpctl_argv) free(inst->zorpctl_argv); free(inst); } static int z_check_instance_name(char *name) { int i; for (i = 0; name[i]; i++) { if (!isalnum(name[i]) && name[i] != '_') return 0; } return isalpha(name[0]); } static int z_check_dir(char *dir, char *owner, char *group, char *perm, int create) { struct passwd *pw = getpwnam(owner); struct group *gr = group ? getgrnam(group) : NULL; uid_t uid = -1; gid_t gid = -1; mode_t mode = perm ? strtol(perm, NULL, 8) : 0700; struct stat st; if (pw) uid = pw->pw_uid; if (gr) gid = gr->gr_gid; if (gid == (gid_t) -1 && pw) gid = pw->pw_gid; if (uid == (gid_t) -1 || gid == (gid_t) -1) { z_error(0, "Owner/group not found, owner='%s', group='%s'\n", SAFE_STR(owner), SAFE_STR(group)); return 0; } if (stat(dir, &st) < 0) { if (create) { if ((mkdir(dir, mode) >= 0) && (chown(dir, uid, gid) >= 0) && (chmod(dir, mode) >= 0)) return 1; z_error(0, "Error creating directory, dir='%s', uid='%d', gid='%d', mode='%o', error='%s'\n", dir, uid, gid, perm, strerror(errno)); } return 0; } if (owner && st.st_uid != uid) return 0; if (group && st.st_gid != gid) return 0; if (perm && ((st.st_mode & 07777) != mode)) return 0; return 1; } static void z_check_pidfile_dir(void) { if (pidfile_dir_owner || pidfile_dir_group || pidfile_dir_perm) { z_check_dir(pidfile_dir, pidfile_dir_owner, pidfile_dir_group, pidfile_dir_perm, 1); } } static int z_check_config_dir(void) { if (check_perms) { if (!z_check_dir(config_dir, config_dir_owner, config_dir_group, config_dir_perm, 0)) { z_error(0, "Config directory has invalid permissions, expected: dir='%s', owner='%s', group='%s', perm='%s'\n", config_dir, SAFE_STR(config_dir_owner), SAFE_STR(config_dir_group), SAFE_STR(config_dir_perm)); return 0; } } return 1; } static int z_parse_args(ZInstance *inst, char *zorp_args, char *zorpctl_args) { static int process_limit = 0; static int no_auto_start = 0, enable_core = 0, parallel_instances = 1; static GOptionEntry zorpctl_options[] = { { "auto-restart", 'A', 0, G_OPTION_ARG_NONE, &auto_restart, NULL, NULL }, { "no-auto-restart", 'a', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &auto_restart, NULL, NULL }, { "no-auto-start", 'S', 0, G_OPTION_ARG_NONE, &no_auto_start, NULL, NULL }, { "process-limit", 'p', 0, G_OPTION_ARG_INT, &process_limit, NULL, NULL }, { "enable-core", 'c', 0, G_OPTION_ARG_NONE, &enable_core, NULL, NULL }, { "parallel-instances", 'P', 0, G_OPTION_ARG_INT, ¶llel_instances, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; static int threads = 1000; static GOptionEntry zorp_options[] = { { "threads", 't', 0, G_OPTION_ARG_INT, &threads, NULL, NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; int res = 0; char *buf = NULL; GOptionContext *ctx = NULL; IGNORE_UNUSED_RESULT(asprintf(&buf, "%s --as %s %s %s", ZORP, inst->real_name, zorp_args, SAFE_STR_EMPTY(zorp_append_args))); if (!g_shell_parse_argv(buf, &inst->zorp_argc, &inst->zorp_argv, NULL)) { z_error(1, "Invalid zorp argument list: %s\n", zorp_args); goto finish; } g_free(buf); IGNORE_UNUSED_RESULT(asprintf(&buf, "zorpctl %s %s", SAFE_STR_EMPTY(zorpctl_args), SAFE_STR_EMPTY(zorpctl_append_args))); if (!g_shell_parse_argv(buf, &inst->zorpctl_argc, &inst->zorpctl_argv, NULL)) { z_error(1, "Invalid zorpctl argument list: %s\n", zorpctl_args); goto finish; } g_free(buf); buf = NULL; if (inst->zorp_argv) { ctx = g_option_context_new(NULL); g_option_context_add_main_entries(ctx, zorp_options, NULL); g_option_context_set_ignore_unknown_options(ctx, TRUE); g_option_context_set_help_enabled(ctx, FALSE); if (!g_option_context_parse(ctx, &inst->zorp_argc, &inst->zorp_argv, NULL)) goto finish; g_option_context_free(ctx); /* NOTE: we need to reconstruct the command line as * g_option_context_parse() removes parsed entries, such as --threads * * FIXME: this leaks memory. * */ IGNORE_UNUSED_RESULT(asprintf(&buf, "%s --as %s %s %s", ZORP, inst->real_name, zorp_args, SAFE_STR_EMPTY(zorp_append_args))); if (!g_shell_parse_argv(buf, &inst->zorp_argc, &inst->zorp_argv, NULL)) { z_error(1, "Invalid zorp argument list: %s\n", zorp_args); goto finish; } g_free(buf); buf = NULL; ctx = NULL; process_limit_sum += threads + 5; } inst->auto_restart = auto_restart; inst->process_limit = process_limit = MAX(process_limit_min, threads + 5); if (inst->zorpctl_argv) { ctx = g_option_context_new(NULL); g_option_context_add_main_entries(ctx, zorpctl_options, NULL); g_option_context_set_ignore_unknown_options(ctx, TRUE); g_option_context_set_help_enabled(ctx, FALSE); if (!g_option_context_parse(ctx, &inst->zorpctl_argc, &inst->zorpctl_argv, NULL)) goto finish; g_option_context_free(ctx); ctx = NULL; inst->auto_restart = auto_restart; inst->process_limit = process_limit; inst->no_auto_start = no_auto_start; inst->enable_core = enable_core; inst->parallel_instances = parallel_instances; if (inst->zorpctl_argc > 1) { z_error(1, "Junk found at the end of the arg list: %s\n", inst->zorpctl_argv[1]); goto finish; } } res = 1; finish: if (buf) g_free(buf); if (ctx) g_option_context_free(ctx); return res; } static int z_parse_instances(void) { FILE *instf; char line[MAX_ZORPCTL_LINE_LENGTH]; ZInstance *inst, **last = &instances; int len, i; char *dashdash, *zorp_args, *zorpctl_args; instances = NULL; instf = fopen(ZORP_INSTANCES_CONF, "r"); if (!instf) { /*LOG *This message indicates that Zorp was unable to open the instances file. *Check the permissions of your instances file. */ z_error(1, "Error opening instances file: %s\n", ZORP_INSTANCES_CONF); return 0; } while (fgets(line, sizeof(line), instf)) { chomp(line); if (line[0] == 0 || line[0] == '#') continue; len = strlen(line); inst = calloc(sizeof(ZInstance), 1); i = 0; while (i < len && !isspace(line[i])) i++; inst->role = NONE; inst->real_name = strduplen(line, i); if (!z_check_instance_name(inst->real_name)) { z_error(1, "Invalid instance name: %s\n", inst->real_name); z_instance_free(inst); return 0; } while (i < len && isspace(line[i])) i++; dashdash = strstr(&line[i], " -- "); if (dashdash) { zorp_args = strduplen(&line[i], dashdash - &line[i]); zorpctl_args = strdup(dashdash + 4); } else { zorp_args = strdup(&line[i]); zorpctl_args = NULL; } if (!z_parse_args(inst, zorp_args, zorpctl_args)) { z_error(1, "Invalid argument list at instance: %s\n", inst->real_name); z_instance_free(inst); return 0; } free(zorp_args); free(zorpctl_args); inst->next = *last; *last = inst; last = &inst->next; } return 1; } static ZInstance * z_search_instance(char *name) { ZInstance *p, *ret = NULL; char *instance_name = strdup(name); char *hash = strrchr(instance_name, '#'); if (hash) *hash = 0; for (p = instances; p; p = p->next) { if (strcmp(instance_name, p->real_name) == 0) { ret = p; break; } } if (hash && ret) { ret->virtual_name = name; /* if the number after the # is 0, it's a master instance, otherwise it's a slave */ ret->role = atoi(hash+1) ? SLAVE : MASTER; } g_free(instance_name); return ret; } static int z_parse_config_line_int(char *var, char *name, char *value, int *result) { if (strcmp(name, var) == 0) { char quote = value[0]; char *end; if (quote == '"' || quote == '\'') { *result = strtol(value + 1, &end, 10); if (*end != quote) return 0; } else { *result = strtol(value, &end, 10); if (*end != 0) return 0; } return 1; } return 0; } static int z_parse_config_line_double(char *var, char *name, char *value, double *result) { if (strcmp(name, var) == 0) { char quote = value[0]; char *end; if (quote == '"' || quote == '\'') { *result = strtod(value + 1, &end); if (*end != quote) return 0; } else { *result = strtod(value, &end); if (*end != 0) return 0; } return 1; } return 0; } static int z_parse_config_line_str(char *var, char *name, char *value, char **result) { if (strcmp(name, var) == 0) { char quote = value[0]; if (quote == '"' || quote == '\'') { char *p, *dst; *result = malloc(strlen(value)); dst = *result; for (p = value + 1; *p; p++) { if (*p == '\\') { *dst = *(p+1); p++; } else if (*p == quote) { break; } else { *dst = *p; } dst++; } if (*p != 0 && *(p+1)) { /* invalid quotation marks */ free(*result); *result = NULL; return 0; } *dst = 0; } else *result = strdup(value); return 1; } return 0; } static int z_parse_config(void) { FILE *cfgf; char line[256], *value, *name, *eq; int lineno = 0; int res = 1; cfgf = fopen(ZORP_ZORPCTL_CONF, "r"); if (!cfgf) return 0; while (fgets(line, sizeof(line), cfgf)) { lineno++; chomp(line); if (line[0] == 0 || line[0] == '#') continue; eq = strchr(line, '='); if (!eq) { z_error(0, "Invalid zorpctl.conf line at %d: %s\n", lineno, line); return 0; } *eq = 0; value = eq + 1; name = line; if (!(z_parse_config_line_int("AUTO_RESTART", name, value, &auto_restart) || z_parse_config_line_int("AUTO_RESTART_TIME_THRESHOLD", name, value, &auto_restart_time_threshold) || z_parse_config_line_int("AUTO_RESTART_MAX_COUNT", name, value, &auto_restart_max_count) || z_parse_config_line_double("AUTO_RESTART_DELAY", name, value, &auto_restart_delay) || z_parse_config_line_int("START_CHECK_TIMEOUT", name, value, &start_check_timeout) || z_parse_config_line_int("START_WAIT_TIMEOUT", name, value, &start_wait_timeout) || z_parse_config_line_double("STOP_CHECK_DELAY", name, value, &stop_check_delay) || z_parse_config_line_int("STOP_CHECK_TIMEOUT", name, value, &stop_check_timeout) || z_parse_config_line_int("PROCESS_LIMIT_THRESHOLD", name, value, &process_limit_threshold) || z_parse_config_line_int("PROCESS_LIMIT_RESERVE", name, value, &process_limit_reserve) || z_parse_config_line_int("PROCESS_LIMIT_MIN", name, value, &process_limit_min) || z_parse_config_line_int("CHECK_PERMS", name, value, &check_perms) || z_parse_config_line_str("CONFIG_DIR", name, value, &config_dir) || z_parse_config_line_str("CONFIG_DIR_OWNER", name, value, &config_dir_owner) || z_parse_config_line_str("CONFIG_DIR_GROUP", name, value, &config_dir_group) || z_parse_config_line_str("CONFIG_DIR_MODE", name, value, &config_dir_perm) || z_parse_config_line_str("APPEND_ARGS", name, value, &zorp_append_args) || z_parse_config_line_str("ZORP_APPEND_ARGS", name, value, &zorp_append_args) || z_parse_config_line_str("ZORPCTL_APPEND_ARGS", name, value, &zorpctl_append_args) || z_parse_config_line_str("PIDFILE_DIR", name, value, &pidfile_dir) || z_parse_config_line_str("PIDFILE_DIR_OWNER", name, value, &pidfile_dir_owner) || z_parse_config_line_str("PIDFILE_DIR_GROUP", name, value, &pidfile_dir_group) || z_parse_config_line_str("PIDFILE_DIR_MODE", name, value, &pidfile_dir_perm))) { z_error(0, "Unknown zorpctl.conf directive at %d: %s\n", lineno, line); res = 0; } } if (process_limit_threshold != -1) { z_error(0, "The use of PROCESS_LIMIT_THRESHOLD in zorpctl.conf is deprecated, its value is ignored\n"); res = 0; } if (auto_restart_time_threshold != -1 || auto_restart_max_count != -1 || start_check_timeout != -1 || auto_restart_delay != -1.0) { z_error(0, "The use of AUTO_RESTART_DELAY, AUTO_RESTART_TIME_THRESHOLD, AUTO_RESTART_MAX_COUNT and START_CHECK_TIMEOUT in zorpctl.conf is deprecated, their values are ignored\n"); res = 0; } return res; } /** * z_parse_line_item: * @pp[in,out]: A pointer to the first character in a buffer where the parser should start. * It will point to the next item if any. * @prefix[in]: The prefix of the value which should be read. (OPTIONAL) * @item[out]: This parameter will contain the parsed item. More specially a pointer to the * starting place of the item in the @pp. * * This function get the value part of specially crafted string buffer which contains pairs. * This function will alter the @pp buffer. It will insert NUL characters into it. * The prefix is optional. The items should be separated by whitespaces and no escaping is allowed. * If * * Returns: TRUE on successful read and targeted item's length > 0, and */ static gboolean z_parse_kzorp_entry_item(gchar **pp, const gchar *prefix, char **item) { gchar *p = *pp; gsize prefix_len = 0; gboolean eol = FALSE; if (! *p) return FALSE; if (prefix) prefix_len = strlen(prefix); if (prefix_len) { if (strncmp(p, prefix, prefix_len)) return FALSE; } p += prefix_len; *item = p; while (*p != ' ' && *p != '\t' && *p != 0) ++p; if (*p) { while (*p == ' ' || *p == '\t') *p++ = 0; if (! *p) eol = TRUE; } *pp = p; return **item || !eol; } /** * z_process_kzorp_entries: * @ins: Zorp Instance * @user_data: not used * * This function parses the '/proc/net/nf_kzorp' file line by line, or if it doesn't * exist writes an error message and immediately returns. * * Returns: 0 on error, 1 otherwise */ static int z_process_kzorp_entries(ZInstance *ins, gpointer user_data G_GNUC_UNUSED) { gchar buffer[4096]; gchar *p; FILE *file; gboolean initialized = FALSE; gint res = 0; gchar *dpt, *svc, *szone, *czone, *tmp; gchar *proto, *src, *dst, *src2, *dst2; gchar *sport, *dport, *sport2, *dport2; gchar *state, *instance, *sid; if ((file = fopen("/proc/net/nf_kzorp", "r")) == NULL) { if (errno == ENOENT) { /* No such file exists: kzorp is most probably not available, so do * not consider this an error. */ res = 1; } else { z_error(0, "Unable to open /proc/net/nf_kzorp. Kernel is too old or kzorp is not loaded.\n"); } goto error; } while (1) { if (!fgets(buffer, 4096, file) ) { if (!feof(file)) { z_error(0, "Error while reading from /proc/net/nf_kzorp.\n"); fclose(file); goto error; } else break; } p = buffer; if (!z_parse_kzorp_entry_item(&p, "instance=", &instance) || /* different service */ strcmp(instance, ins->real_name) || !z_parse_kzorp_entry_item(&p, "sid=", &sid) || !z_parse_kzorp_entry_item(&p, "dpt=", &dpt) || !z_parse_kzorp_entry_item(&p, "svc=", &svc) || !z_parse_kzorp_entry_item(&p, "czone=", &czone) || !z_parse_kzorp_entry_item(&p, "szone=", &szone) || !z_parse_kzorp_entry_item(&p, NULL, &tmp) || !z_parse_kzorp_entry_item(&p, NULL, &tmp) || !z_parse_kzorp_entry_item(&p, NULL, &proto) || /* skip these */ !z_parse_kzorp_entry_item(&p, NULL, &tmp) || !z_parse_kzorp_entry_item(&p, NULL, &tmp)) continue; if (!strcmp(proto, "tcp")) { if (!z_parse_kzorp_entry_item(&p, NULL, &state) || strcmp(state, "ESTABLISHED")) continue; } if (!z_parse_kzorp_entry_item(&p, "src=", &src) || !z_parse_kzorp_entry_item(&p, "dst=", &dst) || !z_parse_kzorp_entry_item(&p, "sport=", &sport) || !z_parse_kzorp_entry_item(&p, "dport=", &dport) || /* skip these */ !z_parse_kzorp_entry_item(&p, NULL, &tmp) || !z_parse_kzorp_entry_item(&p, NULL, &tmp) || /* It is the other direction. Source and destination are switched */ !z_parse_kzorp_entry_item(&p, "src=", &src2) || !z_parse_kzorp_entry_item(&p, "dst=", &dst2) || !z_parse_kzorp_entry_item(&p, "sport=", &sport2) || !z_parse_kzorp_entry_item(&p, "dport=", &dport2)) continue; if (!initialized) { printf("conns.%s: None\n", svc); initialized = TRUE; } printf("conns.%s.%s: None\n", svc, sid); printf("conns.%s.%s.0: None\n", svc, sid); printf("conns.%s.%s.0.0: None\n", svc, sid); printf("conns.%s.%s.0.0.client_address: AF_INET(%s:%s)\n", svc, sid, src, sport); printf("conns.%s.%s.0.0.client_local: AF_INET(%s:%s)\n", svc, sid, dst, dport); printf("conns.%s.%s.0.0.client_zone: %s\n", svc, sid, czone); printf("conns.%s.%s.0.0.proxy_class: PFService\n", svc, sid); printf("conns.%s.%s.0.0.server_address: AF_INET(%s:%s)\n", svc, sid, src2, sport2); printf("conns.%s.%s.0.0.server_local: AF_INET(%s:%s)\n", svc, sid, dst2, dport2); printf("conns.%s.%s.0.0.server_zone: %s\n", svc, sid, szone); printf("conns.%s.%s.0.0.session_id: svc/%s:%s\n", svc, sid, svc, sid); printf("conns.%s.%s.0.0.protocol: %s\n", svc, sid, proto); } fclose(file); res = 1; error: return res; } static int z_get_counter(ZInstance *inst, const char *var_name) { ZSzigContext *ctx; char result[256]; int success, thread_count; ctx = z_szig_context_new(inst->virtual_name); if (!ctx) return -1; success = z_szig_get_value(ctx, var_name, result, sizeof(result)); z_szig_context_destroy(ctx); if (!success) return -1; thread_count = strtol(result, NULL, 10); return thread_count; } static int z_get_thread_count(ZInstance *inst) { return z_get_counter(inst, "stats.threads_running"); } static void z_setup_limits(ZInstance *inst) { struct rlimit limit; #ifdef RLIMIT_NPROC limit.rlim_cur = limit.rlim_max = MAX(inst->process_limit, process_limit_sum + process_limit_reserve); setrlimit(RLIMIT_NPROC, &limit); #endif } static int z_start_instance(ZInstance *inst) { pid_t child; int status; int res = 1; child = fork(); if (child == 0) { char **new_zorp_argv; new_zorp_argv = malloc((inst->zorp_argc + 6) * sizeof(char *)); memcpy(new_zorp_argv, inst->zorp_argv, (inst->zorp_argc + 1) * sizeof(char *)); inst->zorp_argv = new_zorp_argv; if (inst->enable_core) { inst->zorp_argv[inst->zorp_argc++] = "--enable-core"; } if (!inst->auto_restart) { inst->zorp_argv[inst->zorp_argc++] = "--process-mode"; inst->zorp_argv[inst->zorp_argc++] = "background"; } switch (inst->role) { case MASTER: inst->zorp_argv[inst->zorp_argc++] = "--master"; inst->zorp_argv[inst->zorp_argc++] = inst->virtual_name; break; case SLAVE: inst->zorp_argv[inst->zorp_argc++] = "--slave"; inst->zorp_argv[inst->zorp_argc++] = inst->virtual_name; break; case NONE: break; } inst->zorp_argv[inst->zorp_argc] = NULL; z_setup_limits(inst); setsid(); execvp(ZORP, inst->zorp_argv); exit(1); } if (start_wait_timeout) { z_alarm_request(start_wait_timeout); if (waitpid(child, &status, 0) < 0) { res = 0; if (z_alarm_fired()) { /* timed out */ z_error(1, "Timeout waiting for Zorp instance to start up, instance='%s'\n", inst->virtual_name); } } else if (status != 0) { z_error(1, "Zorp instance startup failed, instance='%s', rc='%d'\n", inst->virtual_name, status); res = 0; } } return res; } static int z_process_start_instance(ZInstance *inst, void *user_data UNUSED) { int stale; pid_t pid; int res = 1; if (z_instance_running(inst, &pid, &stale)) { z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_SUCCESS_WARN; return 0; } if (stale) z_instance_remove_stale_pidfile(inst->virtual_name); if (!inst->no_auto_start) res = z_start_instance(inst); if (res) z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_SUCCESS; else z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_GENERAL_FAILURE; return res; } static int z_process_force_start_instance(ZInstance *inst, void *user_data UNUSED) { inst->no_auto_start = 0; return z_process_start_instance(inst, user_data); } static int z_process_stop_instance(ZInstance *inst, void *user_data) { int stale, killed; int signo = (long) user_data; time_t prev_check, now; pid_t pid; if (!z_instance_running(inst, &pid, &stale)) { z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_SUCCESS_WARN; return 0; } if (stale) { z_instance_remove_stale_pidfile(inst->virtual_name); } else { kill(pid, signo); prev_check = now = time(NULL); killed = !z_instance_running(inst, &pid, &stale); for (killed = 0; (now - prev_check) < stop_check_timeout; now = time(NULL)) { usleep((unsigned long)(stop_check_delay * 1e6)); if ((killed = !z_instance_running(inst, &pid, &stale))) break; } if (!killed) { z_error(1, "Zorp instance did not exit in time (instance='%s', pid='%d', signo='%d', timeout='%d')\n", SAFE_STR(inst->virtual_name), pid, signo, stop_check_timeout); z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_GENERAL_FAILURE; return 0; } if (signo == SIGKILL) z_instance_remove_stale_pidfile(inst->virtual_name); } z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_SUCCESS; return 1; } static int z_process_restart_instance(ZInstance *inst, void *user_data) { z_process_stop_instance(inst, user_data); return z_process_start_instance(inst, user_data); } static int z_process_signal_instance(ZInstance *inst, void *user_data) { int stale; int signo = (long) user_data; pid_t pid; if (!z_instance_running(inst, &pid, &stale)) { return 0; } kill(pid, signo); return 1; } static int z_process_status_instance(ZInstance *inst, void *user_data) { int stale; pid_t pid; time_t starttime; int *status_flags = (int *) user_data; ZSzigContext *ctx; char result[16384]; char policy_file[256]; time_t timestamp_szig = 0, timestamp_stat = 0, timestamp_reload = 0; printf("Instance %s: ", inst->virtual_name); if (!z_instance_running(inst, &pid, &stale)) { if (stale) { printf("stale pidfile, pid %d\n", pid); } else { printf("not running\n"); } return 1; } ctx = z_szig_context_new(inst->virtual_name); if (ctx) { struct stat st; result[0] = '\0'; if (!z_szig_get_value(ctx, "info.policy.file", policy_file, sizeof(policy_file)) || memcmp(result, "None", 4) == 0) goto szig_error; if (!stat(policy_file, &st)) timestamp_stat = st.st_mtime; if (!z_szig_get_value(ctx, "info.policy.file_stamp", result, sizeof(result)) || memcmp(result, "None", 4) == 0) goto szig_error; timestamp_szig = (time_t)atol(result); if (!z_szig_get_value(ctx, "info.policy.reload_stamp", result, sizeof(result)) || memcmp(result, "None", 4) == 0) goto szig_error; timestamp_reload = (time_t)atol(result); } else goto szig_error; printf("running, %s%d threads active, pid %d\n", (timestamp_stat != timestamp_szig) ? "policy NOT reloaded, " : "", z_get_thread_count(inst), pid); if (status_flags && (*status_flags & ZORPCTL_STATUS_VERBOSE)) { char starttime_str[32], loadedtime_str[32]; double jps, realtime, usertime, systime; int realmin, usermin, sysmin; ZProcInfo pi; memset(&pi, 0, sizeof(ZProcInfo)); z_get_proc_info(pid, &pi); jps = z_get_jiffies_per_sec(); usertime = pi.utime / jps; usermin = (int)(usertime / 60); usertime -= (usermin * 60); systime = pi.stime / jps; sysmin = (int)(systime / 60); systime -= (sysmin * 60); realtime = usertime + systime; realmin = (int)(realtime / 60); realtime -= (realmin * 60); starttime = time(NULL) - z_get_uptime() + (pi.starttime / jps); strftime(starttime_str, sizeof(starttime_str) - 1, "%Y-%m-%d %H:%M:%S", localtime(&starttime)); strftime(loadedtime_str, sizeof(loadedtime_str) - 1, "%Y-%m-%d %H:%M:%S", localtime(×tamp_reload)); printf(" started at: %s\n", starttime_str); printf(" policy: file=%s, loaded=%s\n", policy_file, loadedtime_str); printf(" cpu: real=%02d:%06.3lf, user=%02d:%06.3lf, sys=%02d:%06.3lf\n", realmin, realtime, usermin, usertime, sysmin, systime); printf(" memory: vsz=%lu, rss=%ld\n", pi.vsize >> 10, pi.rss << 2); } goto exit; szig_error: printf("error querying SZIG information\n"); exit: if (ctx) z_szig_context_destroy(ctx); return 1; } static int z_process_authorize(ZInstance *inst, void *user_data) { int stale; pid_t pid; ZSzigContext *ctx; char result[16384]; gchar **params = (gchar **) user_data; printf("Instance %s: ", inst->virtual_name); if (!z_instance_running(inst, &pid, &stale)) { if (stale) { printf("stale pidfile, pid %d\n", pid); } else { printf("not running\n"); } return 1; } ctx = z_szig_context_new(inst->virtual_name); if (ctx) { gboolean accept = params[0] != NULL; z_szig_authorize(ctx, params[ accept ? 0 : 1 ], accept, params[2], result, sizeof(result)); printf("%s\n", result); } else goto szig_error; goto exit; szig_error: printf("error querying SZIG information\n"); exit: if (ctx) z_szig_context_destroy(ctx); return 1; } static void z_szig_walk(ZSzigContext *ctx, const char *root) { char result[16384]; if (strlen(root) > 0) { z_szig_get_value(ctx, root, result, sizeof(result)); printf("%s: %s\n", root, result); } z_szig_get_child(ctx, root, result, sizeof(result)); if (strcmp(result, "None") != 0) { /* walk all children, depth first */ z_szig_walk(ctx, result); /* siblings */ z_szig_get_sibling(ctx, result, result, sizeof(result)); while (strcmp(result, "None") != 0) { z_szig_walk(ctx, result); z_szig_get_sibling(ctx, result, result, sizeof(result)); } } } static int z_process_szig_walk_instance(ZInstance *inst, void *user_data) { ZSzigContext *ctx; char *root = (char *) user_data; printf("Instance %s: ", inst->virtual_name); ctx = z_szig_context_new(inst->virtual_name); if (!ctx) { printf("not running\n"); z_szig_context_destroy(ctx); return 0; } else { printf("walking\n"); } z_szig_walk(ctx, root); z_szig_context_destroy(ctx); return 1; } static int z_process_log_func(ZInstance *inst, char *cmd, char *param) { ZSzigContext *ctx; int res = 0; ctx = z_szig_context_new(inst->virtual_name); if (ctx) { if (z_szig_logging(ctx, cmd, param, NULL, 0)) res = 1; z_szig_context_destroy(ctx); } else { z_error(1, "Error connecting to Zorp SZIG socket, instance='%s', error='%s'\n", inst->virtual_name, strerror(errno)); } return res; } static int z_process_log_vinc_instance(ZInstance *inst, void *user_data UNUSED) { return z_process_log_func(inst, "VINC", "1"); } static int z_process_log_vdec_instance(ZInstance *inst, void *user_data UNUSED) { return z_process_log_func(inst, "VDEC", "1"); } static int z_process_log_vset_instance(ZInstance *inst, void *user_data) { char buf[16]; snprintf(buf, sizeof(buf), "%d", *(int *) user_data); return z_process_log_func(inst, "VSET", buf); } static int z_process_log_logspec_instance(ZInstance *inst, void *user_data) { return z_process_log_func(inst, "SETSPEC", (char *) user_data); } static int z_process_log_status_instance(ZInstance *inst, void *user_data UNUSED) { ZSzigContext *ctx; int res = 0; char verb_level[16]; char spec[128]; ctx = z_szig_context_new(inst->virtual_name); if (ctx) { if (z_szig_logging(ctx, "VGET", "", verb_level, sizeof(verb_level))) res = 1; if (!res || !z_szig_logging(ctx, "GETSPEC", "", spec, sizeof(spec))) res = 0; if (res) { printf("Instance: %s: verbose_level='%s', logspec='%s'\n", inst->virtual_name, verb_level, spec); } z_szig_context_destroy(ctx); } if (!res) { printf("Instance: %s, error querying log information\n", inst->virtual_name); } return res; } /* DEADLOCKCHECK command */ static int z_process_deadlockcheck_func(ZInstance *inst, char *cmd) { ZSzigContext *ctx; int res = 0; ctx = z_szig_context_new(inst->virtual_name); if (ctx) { z_szig_deadlockcheck(ctx, cmd, NULL, 0); res = 1; z_szig_context_destroy(ctx); } else { z_error(1, "Error connecting to Zorp SZIG socket, instance='%s', error='%s'\n", inst->virtual_name, strerror(errno)); } return res; } static int z_process_deadlockcheck_enable_instance(ZInstance *inst, void *user_data UNUSED) { return z_process_deadlockcheck_func(inst, "ENABLE"); } static int z_process_deadlockcheck_disable_instance(ZInstance *inst, void *user_data UNUSED) { return z_process_deadlockcheck_func(inst, "DISABLE"); } static int z_process_deadlockcheck_status_instance(ZInstance *inst, void *user_data UNUSED) { ZSzigContext *ctx; int res = 0; char timeout[16]; ctx = z_szig_context_new(inst->virtual_name); if (ctx) { if (z_szig_deadlockcheck(ctx, "GET", timeout, sizeof(timeout))) res = 1; if (res) { printf("Instance: %s: deadlockcheck='%s'\n", inst->virtual_name, timeout); } z_szig_context_destroy(ctx); } if (!res) { printf("Instance: %s, error querying deadlock checker information\n", inst->virtual_name); } return res; } static inline int z_process_reload_inline(ZInstance *inst, gboolean suppress_message) { ZSzigContext *ctx; int res = 0; ctx = z_szig_context_new(inst->virtual_name); if (ctx) { if (z_szig_reload(ctx, NULL, NULL, 0) && z_szig_reload(ctx, "RESULT", NULL, 0)) res = 1; z_szig_context_destroy(ctx); } else { if (!suppress_message) z_error(1, "Error connecting to Zorp SZIG socket, instance='%s'", inst->virtual_name); } return res; } static inline int z_process_reload(ZInstance *inst, void *user_data UNUSED) { int res; res = z_process_reload_inline(inst, FALSE); return res; } static inline int z_process_reload_or_restart(ZInstance *inst, void *user_data UNUSED) { int res = 0; res = z_process_reload_inline(inst, TRUE); if ( res != 1) { if (!z_process_restart_instance(inst, NULL)) { z_error(1, "Both reload and restart failed, instance='%s'", inst->virtual_name); res = 0; } else { res = 1; } } return res; } static inline int z_process_coredump(ZInstance *inst, void *user_data UNUSED) { int res = 0; ZSzigContext *ctx; ctx = z_szig_context_new(inst->virtual_name); if (ctx) { res = z_szig_coredump(ctx); z_szig_context_destroy(ctx); } if (!res) { printf("Instance: %s, error creating core dump\n", inst->virtual_name); } return res; } static int z_process_args(char *ident, int argc, char *argv[], ZCmdFunc func, void *user_data, int display_instances) G_GNUC_WARN_UNUSED_RESULT; static int z_process_args_withmsg( const char * msg, char *ident, int argc, char *argv[], ZCmdFunc func, void *user_data, int display_instances) { int res; printf("%s", msg); res = z_process_args(ident, argc, argv, func, user_data, display_instances); printf("\n"); return res; } static int z_process_args_run_cmd_instance(ZCmdFunc func, void *user_data, ZInstance *inst) { int success = 1; /* an instance will have virtual_name already set if: * 1) the instance is non-parallel (parallel_instances == 1), in this * case the virtual instance name is the same as the real instance * name * 2) the instance is parallel (parallel_instances > 1), and the user * supplied a virtual instance name (on that has a '#') as an * argument for the command to be executed. In this case * virtual_name contains the name supplied. */ if (!inst->virtual_name) { int virt_nr; char virtual_name[MAX_ZORPCTL_LINE_LENGTH]; for (virt_nr = 0; virt_nr < inst->parallel_instances; ++virt_nr) { inst->role = virt_nr ? SLAVE : MASTER; snprintf(virtual_name, MAX_ZORPCTL_LINE_LENGTH, "%s#%d", inst->real_name, virt_nr); inst->virtual_name = virtual_name; success &= func(inst, user_data); } inst->virtual_name = NULL; } else { success = func(inst, user_data); } return success; } static int z_process_args_run_cmd_str(ZCmdFunc func, void *user_data, char *instance_name, const char *ident) { int success = 1; ZInstance *inst = z_search_instance(instance_name); if (inst) { success = z_process_args_run_cmd_instance(func, user_data, inst); } else { z_error(1, "%s: No such instance: %s\n", ident, instance_name); } return success; } static int z_process_args(char *ident, int argc, char *argv[], ZCmdFunc func, void *user_data, int display_instances) { ZInstance *inst; int success_all = 1; int i; if (argc == 0) { for (inst = instances; inst; inst = inst->next) { z_ctl_cmd_proc_status = Z_CTL_CMD_PROC_STATUS_INVALID; int success = z_process_args_run_cmd_instance(func, user_data, inst); if (!success && z_ctl_cmd_proc_status > Z_CTL_CMD_PROC_STATUS_SUCCESS_WARN) success_all = 0; if (display_instances) printf("%s%s", inst->real_name, success ? " " : "! "); } } else { if (argv[0][0] == '@') { FILE *inst_list; gchar inst_buf[128]; /* list of instances is in a file */ inst_list = fopen(&argv[0][1], "r"); if (inst_list) { while (fgets(inst_buf, sizeof(inst_buf), inst_list)) { int success; chomp(inst_buf); success = z_process_args_run_cmd_str(func, user_data, inst_buf, ident); success_all &= success; if (display_instances) printf("%s%s", inst_buf, success ? " " : "! "); } fclose(inst_list); } else { z_error(1, "Error opening instance list file: %s.", &argv[0][1]); success_all = 0; } } else { for (i = 0; i < argc; i++) { int success = z_process_args_run_cmd_str(func, user_data, argv[i], ident); success_all &= success; if (display_instances) printf("%s%s", argv[i], success ? " " : "! "); } } } return success_all ? 0 : 1; } /* command implementations */ static int z_cmd_start(int argc, char *argv[]) { return z_process_args_withmsg("Starting Zorp Firewall Suite: ", "start", argc-2, &argv[2], z_process_start_instance, NULL, 1); } static int z_cmd_force_start(int argc, char *argv[]) { return z_process_args_withmsg("Starting Zorp Firewall Suite: ", "start", argc-2, &argv[2], z_process_force_start_instance, NULL, 1); } static int z_cmd_stop(int argc, char *argv[]) { return z_process_args_withmsg("Stopping Zorp Firewall Suite: ", "stop", argc-2, &argv[2], z_process_stop_instance, (void *) SIGTERM, 1); } static int z_cmd_force_stop(int argc, char *argv[]) { return z_process_args_withmsg("Stopping Zorp Firewall Suite: ", "stop", argc-2, &argv[2], z_process_stop_instance, (void *) SIGKILL, 1); } static int z_cmd_restart(int argc, char *argv[]) { return z_process_args_withmsg("Restarting Zorp Firewall Suite: ", "restart", argc-2, &argv[2], z_process_restart_instance, (void *) SIGTERM, 1); } static int z_cmd_force_restart(int argc, char *argv[]) { return z_process_args_withmsg("Restarting Zorp Firewall Suite: ", "restart", argc-2, &argv[2], z_process_restart_instance, (void *) SIGKILL, 1); } static int z_cmd_reload(int argc, char *argv[]) { return z_process_args_withmsg("Reloading Zorp Firewall Suite: ", "reload", argc-2, &argv[2], z_process_reload, NULL, 1); } static int z_cmd_reload_or_restart(int argc, char *argv[]) { return z_process_args_withmsg("Reloading or Restarting Zorp Firewall Suite: ", "reload-or-restart", argc-2, &argv[2], z_process_reload_or_restart, NULL, 1); } static int z_cmd_coredump(int argc, char *argv[]) { return z_process_args_withmsg("Creating Zorp core dumps: ", "coredump", argc - 2, &argv[2], z_process_coredump, NULL, 1); } static int z_cmd_status(int argc, char *argv[]) { int processed_args = 2; int status_flags = 0; if ((argc > processed_args) && (strcmp("-v", argv[processed_args]) == 0 || strcmp("--verbose", argv[processed_args]) == 0)) { status_flags = ZORPCTL_STATUS_VERBOSE; processed_args++; } return z_process_args("status", argc - processed_args, &argv[processed_args], z_process_status_instance, &status_flags, 0); } static int z_cmd_authorize(int argc, char *argv[]) { int res = 0; GOptionContext *ctx; GError *error = NULL; gchar *auth_accept_sid = NULL; gchar *auth_reject_sid = NULL; gchar *auth_description = NULL; GOptionEntry authorize_options[] = { { "accept", 'a', 0, G_OPTION_ARG_STRING, &auth_accept_sid, "Accepts authorization request", "" }, { "reject", 'r', 0, G_OPTION_ARG_STRING, &auth_reject_sid, "Rejects authorization request", "" }, { "description", 'd', 0, G_OPTION_ARG_STRING, &auth_description, "Description of accept/reject for logging ", "" }, { NULL, 0, 0, 0, 0, NULL, NULL }, }; --argc; ++argv; ctx = g_option_context_new(NULL); g_option_context_add_main_entries(ctx, authorize_options, NULL); if (!g_option_context_parse(ctx, &argc, &argv, &error)) { fprintf(stderr, "zorpctl authorize: %s\n", error ? error->message : "Unknown error"); return 1; } g_option_context_free(ctx); if (auth_accept_sid && auth_reject_sid) { fprintf(stderr, "zorpctl authorize: both reject and accept cannot be used\n"); return 1; } if (!auth_accept_sid && !auth_reject_sid) { fprintf(stderr, "zorpctl authorize: either the '--accept' or '--reject' option has to be specified\n"); return 1; } if (!auth_description) { fprintf(stderr, "zorpctl authorize: description is not specified\n"); return 1; } { char *params[3] = { auth_accept_sid, auth_reject_sid, auth_description }; ++argv; res = z_process_args("authorize", 1, argv, z_process_authorize, (void *)params, 0); } return res; } static int z_cmd_version(int argc UNUSED, char *argv[] UNUSED) { execl(ZORP, ZORP, "--version", NULL); return 0; } static int z_cmd_inclog(int argc, char *argv[]) { return z_process_args_withmsg("Raising Zorp loglevel: ", "inclog", argc-2, &argv[2], z_process_signal_instance, (void *) SIGUSR1, 1); } static int z_cmd_declog(int argc, char *argv[]) { return z_process_args_withmsg("Lowering Zorp loglevel: ", "declog", argc-2, &argv[2], z_process_signal_instance, (void *) SIGUSR2, 1); } static int z_cmd_log(int argc, char *argv[]) { static int verbose_set = 0; static const char *logspec = NULL; static gboolean inc_verbosity = FALSE, dec_verbosity=FALSE; static GOptionEntry log_options[] = { { "vinc", 'i', 0, G_OPTION_ARG_NONE, &inc_verbosity, "Increment verbosity level by one", NULL }, { "vdec", 'd', 0, G_OPTION_ARG_NONE, &dec_verbosity, "Decrement verbosity level by one", NULL }, { "vset", 's', 0, G_OPTION_ARG_INT, &verbose_set, "Set verbosity level", "" }, { "log-spec", 'S', 0, G_OPTION_ARG_STRING, &logspec, "Set log specification", "" }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; int res = 1; GOptionContext *ctx = g_option_context_new(NULL); g_option_context_add_main_entries(ctx, log_options, NULL); g_option_context_set_ignore_unknown_options(ctx, FALSE); g_option_context_set_help_enabled(ctx, TRUE); argc--; argv++; if (!g_option_context_parse(ctx, &argc, &argv, NULL)) { z_error(0, "zorpctl log: Invalid argument encountered, use -? or --help for usage details\n"); g_option_context_free(ctx); return 0; } argc--; argv++; if (inc_verbosity || dec_verbosity || verbose_set || logspec) printf("Changing Zorp log settings: "); if (inc_verbosity) res = z_process_args("log", argc, argv, z_process_log_vinc_instance, NULL, 1); else if (dec_verbosity) res = z_process_args("log", argc, argv, z_process_log_vdec_instance, NULL, 1); else if (verbose_set) res = z_process_args("log", argc, argv, z_process_log_vset_instance, &verbose_set, 1); else if (logspec) res = z_process_args("log", argc, argv, z_process_log_logspec_instance, (char *) logspec, 1); else res = z_process_args("log", argc, argv, z_process_log_status_instance, NULL, 0); if (inc_verbosity || dec_verbosity || verbose_set || logspec) printf("\n"); g_option_context_free(ctx); return res; } static int z_cmd_deadlockcheck(int argc, char *argv[]) { static gboolean enable_check = FALSE, disable_check = FALSE; static GOptionEntry deadlockcheck_options[] = { { "enable", 'e', 0, G_OPTION_ARG_NONE, &enable_check, "Enable deadlock checking", NULL }, { "disable", 'd', 0, G_OPTION_ARG_NONE, &disable_check, "Disable deadlock checking", NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; int res = 1; GOptionContext *ctx = g_option_context_new(NULL); g_option_context_add_main_entries(ctx, deadlockcheck_options, NULL); g_option_context_set_ignore_unknown_options(ctx, FALSE); g_option_context_set_help_enabled(ctx, TRUE); argc--; argv++; if (!g_option_context_parse(ctx, &argc, &argv, NULL)) { z_error(0, "zorpctl deadlockcheck: Invalid argument encountered, use -? or --help for usage details\n"); g_option_context_free(ctx); return 0; } argc--; argv++; if (enable_check || disable_check) printf("Changing Zorp deadlock checking settings: "); if (enable_check) res = z_process_args("deadlockcheck", argc, argv, z_process_deadlockcheck_enable_instance, NULL, 1); else if (disable_check) res = z_process_args("deadlockcheck", argc, argv, z_process_deadlockcheck_disable_instance, NULL, 1); else res = z_process_args("deadlockcheck", argc, argv, z_process_deadlockcheck_status_instance, NULL, 0); if (enable_check || disable_check) printf("\n"); g_option_context_free(ctx); return res; } static int z_cmd_szig(int argc, char *argv[]) { static gboolean action_walk; static const char *root = ""; static GOptionEntry szig_options[] = { { "walk", 'w', 0, G_OPTION_ARG_NONE, &action_walk, "Walk the specified tree", NULL }, { "root", 'r', 0, G_OPTION_ARG_STRING, &root, "Set the root node of the walk operation", "" }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; int res; GOptionContext *ctx = g_option_context_new(NULL); g_option_context_add_main_entries(ctx, szig_options, NULL); g_option_context_set_ignore_unknown_options(ctx, FALSE); g_option_context_set_help_enabled(ctx, TRUE); argc--; argv++; if (!g_option_context_parse(ctx, &argc, &argv, NULL)) { z_error(0, "zorpctl szig: Invalid argument encountered, use -? or --help for usage details\n"); g_option_context_free(ctx); return 0; } argc--; argv++; /* currently only the default -w action is supported, but there may be more in the future */ res = z_process_args("szig", argc, argv, z_process_szig_walk_instance, (void *) root, 0); g_option_context_free(ctx); if (!res) res = z_process_args("szig", argc, argv, z_process_kzorp_entries, 0, 0); return res; } static int z_cmd_usage(int argc UNUSED, char *argv[] UNUSED) { int i; printf("Syntax\n" " %s \n\n" "The following commands are available:\n\n", argv[0]); for (i = 0; commands[i].cmd; i++) printf("%-18s %s\n", commands[i].cmd, commands[i].help); return 0; } ZCommand commands[] = { { "start", z_cmd_start, "Starts the specified Zorp instance(s)" }, { "force-start", z_cmd_force_start,"Starts the specified Zorp instance(s) even if they are disabled" }, { "stop", z_cmd_stop, "Stops the specified Zorp instance(s)" }, { "force-stop", z_cmd_force_stop,"Forces the specified Zorp instance(s) to stop (SIGKILL)" }, { "restart", z_cmd_restart, "Restart the specified Zorp instance(s)" }, { "force-restart", z_cmd_force_restart, "Forces the specified Zorp instance(s) to restart (SIGKILL)" }, { "reload", z_cmd_reload, "Reload the specified Zorp instance(s)" }, { "reload-or-restart", z_cmd_reload_or_restart, "Reload or restart the specified Zorp instance(s)" }, { "coredump", z_cmd_coredump, "Create core dumps of the specified Zorp instance(s)" }, { "status", z_cmd_status, "Status of the specified Zorp instance(s).\n" "\t\t For additional information use status -v or --verbose option" }, { "authorize", z_cmd_authorize, "Lists and manages authorizations" }, { "version", z_cmd_version, "Display Zorp version information" }, { "inclog", z_cmd_inclog, "Raise the specified Zorp instance(s) log level by one" }, { "declog", z_cmd_declog, "Lower the specified Zorp instance(s) log level by one" }, { "log", z_cmd_log, "Change and query Zorp log settings" }, { "deadlockcheck", z_cmd_deadlockcheck, "Change and query Zorp deadlock checking settings" }, { "szig", z_cmd_szig, "Display internal information from the given Zorp instance(s)" }, { "help", z_cmd_usage, "Display this screen" }, { NULL, NULL, NULL } }; int main(int argc, char *argv[]) { int i; ZCommand *cmd = NULL; int rc = 0; int config_ok = 1; setvbuf(stdout, NULL, _IONBF, 0); z_setup_signals(); config_ok &= !!z_parse_config(); config_ok &= !!z_parse_instances(); z_check_pidfile_dir(); z_check_config_dir(); if (argc < 2) { z_cmd_usage(argc, argv); return 1; } if ((argv[1][0] == '-') && (argv[1][1]=='-')) argv[1] += 2; for (i = 0; commands[i].cmd; i++) { if (strcmp(commands[i].cmd, argv[1]) == 0) { cmd = &commands[i]; break; } } if (cmd) { rc = cmd->func(argc, argv); } else { z_error(0, "Invalid command: %s\n", argv[1]); rc = 1; } z_dump_errors(); return rc ? rc : !config_ok; } zorp-3.9.5/zorpctl/szig.c000066400000000000000000000127531172670260400153540ustar00rootroot00000000000000#include "szig.h" #include #include #include #include #include #define SZIG_MAX_CMD_LENGTH 256 #define SZIG_MAX_VALUE_LENGTH 16384 static int z_szig_write_request(ZSzigContext *ctx, const char *request) { if (write(ctx->fd, request, strlen(request)) < 0) return 0; return 1; } static int z_szig_read_response(ZSzigContext *ctx, char *response, size_t response_len) { int len; if ((len = read(ctx->fd, response, response_len - 1)) < 0) return 0; response[len] = 0; if (response[len - 1] == '\n') response[len-1] = 0; return 1; } int z_szig_get_value(ZSzigContext *ctx, const char *key, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; snprintf(buf, sizeof(buf), "GETVALUE %s\n", key); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, result, result_len)) return 0; return 1; } int z_szig_get_sibling(ZSzigContext *ctx, const char *key, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; snprintf(buf, sizeof(buf), "GETSBLNG %s\n", key); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, result, result_len)) return 0; return 1; } int z_szig_get_child(ZSzigContext *ctx, const char *key, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; snprintf(buf, sizeof(buf), "GETCHILD %s\n", key); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, result, result_len)) return 0; return 1; } int z_szig_logging(ZSzigContext *ctx, const char *subcmd, const char *param, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; char res_buf[128]; snprintf(buf, sizeof(buf), "LOGGING %s %s\n", subcmd, param); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, res_buf, sizeof(res_buf))) return 0; if (strncmp(res_buf, "FAIL ", 5) == 0) { return 0; } else if (strncmp(res_buf, "OK ", 3) == 0) { if (result) { strncpy(result, res_buf + 3, result_len); res_buf[result_len - 1] = 0; } return 1; } else { return 0; } } int z_szig_deadlockcheck(ZSzigContext *ctx, const char *subcmd, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; char res_buf[128]; snprintf(buf, sizeof(buf), "DEADLOCKCHECK %s\n", subcmd); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, res_buf, sizeof(res_buf))) return 0; if (strncmp(res_buf, "FAIL ", 5) == 0) { return 0; } else if (strncmp(res_buf, "OK ", 3) == 0) { if (result) { strncpy(result, res_buf + 3, result_len); res_buf[result_len - 1] = 0; } return 1; } else { return 0; } } int z_szig_reload(ZSzigContext *ctx, const char *subcmd, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; char res_buf[128]; if (!subcmd) snprintf(buf, sizeof(buf), "RELOAD\n"); else snprintf(buf, sizeof(buf), "RELOAD %s\n", subcmd); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, res_buf, sizeof(res_buf))) return 0; if (strncmp(res_buf, "FAIL ", 5) == 0) { return 0; } else if (strncmp(res_buf, "OK ", 3) == 0) { if (result) { strncpy(result, res_buf + 3, result_len); res_buf[result_len - 1] = 0; } return 1; } else { return 0; } } int z_szig_authorize(ZSzigContext *ctx, const char *instance, int accept, const char *description, char *result, size_t result_len) { char buf[SZIG_MAX_CMD_LENGTH]; char res_buf[128]; if (!instance) return 0; else snprintf(buf, sizeof(buf), "AUTHORIZE %s %s %s\n", accept ? "ACCEPT" : "REJECT", instance, description); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, res_buf, sizeof(res_buf))) return 0; if (strncmp(res_buf, "FAIL ", 5) == 0) { if (result) { strncpy(result, res_buf + 5, result_len); res_buf[result_len - 1] = 0; } return 0; } else if (strncmp(res_buf, "OK ", 3) == 0) { if (result) { strncpy(result, res_buf + 3, result_len); res_buf[result_len - 1] = 0; } return 1; } else { return 0; } } int z_szig_coredump(ZSzigContext *ctx) { char buf[SZIG_MAX_CMD_LENGTH]; char res_buf[128]; snprintf(buf, sizeof(buf), "COREDUMP\n"); if (!z_szig_write_request(ctx, buf)) return 0; if (!z_szig_read_response(ctx, res_buf, sizeof(res_buf))) return 0; if (strncmp(res_buf, "FAIL ", 5) == 0) { return 0; } else if (strncmp(res_buf, "OK ", 3) == 0) { return 1; } else { return 0; } } ZSzigContext * z_szig_context_new(const char *instance_name) { ZSzigContext *ctx; struct sockaddr_un unaddr; int fd; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == -1) return NULL; unaddr.sun_family = AF_UNIX; snprintf(unaddr.sun_path, sizeof(unaddr.sun_path), ZORP_PIDFILEDIR "zorpctl.%s", instance_name); if (connect(fd, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) { close(fd); return NULL; } ctx = calloc(sizeof(ZSzigContext), 1); ctx->fd = fd; return ctx; } void z_szig_context_destroy(ZSzigContext *ctx) { if (ctx) { close(ctx->fd); free(ctx); } } zorp-3.9.5/zorpctl/szig.h000066400000000000000000000021561172670260400153550ustar00rootroot00000000000000#ifndef ZORPCTL_SZIG_H_INCLUDED #define ZORPCTL_SZIG_H_INCLUDED #include "zorpctl.h" #include typedef struct _ZSzigContext { int fd; } ZSzigContext; int z_szig_get_value(ZSzigContext *ctx, const char *key, char *result, size_t result_len); int z_szig_get_sibling(ZSzigContext *ctx, const char *key, char *result, size_t result_len); int z_szig_get_child(ZSzigContext *ctx, const char *key, char *result, size_t result_len); int z_szig_logging(ZSzigContext *ctx, const char *subcmd, const char *param, char *result, size_t result_len); int z_szig_reload(ZSzigContext *ctx, const char *subcmd, char *result, size_t result_len); int z_szig_stop_session(ZSzigContext *ctx, const char *instance, char *result, size_t result_len); int z_szig_authorize(ZSzigContext *ctx, const char *instance, int accept, const char *description, char *result, size_t result_len); int z_szig_coredump(ZSzigContext *ctx); int z_szig_deadlockcheck(ZSzigContext *ctx, const char *subcmd, char *result, size_t result_len); ZSzigContext *z_szig_context_new(const char *instance_name); void z_szig_context_destroy(ZSzigContext *ctx); #endif zorp-3.9.5/zorpctl/zorpctl.conf.sample000066400000000000000000000030441172670260400200510ustar00rootroot00000000000000# # This file is sourced by zorpctl and contains default settings # for various parameters. # # specifies whether --auto-restart is default AUTO_RESTART=1 # wait a starting process to report back for this amount of time, assume the # startup failed if it exceeds this time. START_WAIT_TIMEOUT=10 # The interval (in seconds) to check a stopping Zorp instance at, second # fractions are allowed. STOP_CHECK_DELAY=0.1 # The number of seconds to wait for a stopping Zorp instance STOP_CHECK_TIMEOUT=5 # The minimal process limit PROCESS_LIMIT_MIN=256 # The extra processes to allocate limit for. PROCESS_LIMIT_RESERVE=64 # automatically append this string to each Zorp command line, this was # renamed in Zorp 3.0.3, but the original APPEND_ARGS option still works. ZORP_APPEND_ARGS="" # arguments appended to the zorpctl instance specific options, such as # --enable-core ZORPCTL_APPEND_ARGS="" # whether to check /etc/zorp permissions CHECK_PERMS="1" #CONFIG_DIR=/etc/zorp #CONFIG_DIR_OWNER=root #CONFIG_DIR_GROUP=zorp #CONFIG_DIR_MODE=0750 # directory where Zorp stores its pidfiles #PIDFILE_DIR=/var/run/zorp # set pidfile directory ownership according to the settings below (umask is # applied). # DO not modify this, unless you know what you are doing. PIDFILE_DIR_OWNER=zorp PIDFILE_DIR_GROUP=zorp PIDFILE_DIR_MODE=0770 # Deprecated options, these options should not be used anymore and trigger a # warning in zorpctl if they are used. #PROCESS_LIMIT_THRESHOLD #AUTO_RESTART_TIME_THRESHOLD #AUTO_RESTART_MAX_COUNT #START_CHECK_TIMEOUT #AUTO_RESTART_DELAY zorp-3.9.5/zorpctl/zorpctl.h000066400000000000000000000002111172670260400160640ustar00rootroot00000000000000#ifndef ZORPCTL_ZORPCTL_H_INCLUDED #define ZORPCTL_ZORPCTL_H_INCLUDED #include #define FALSE 0 #define TRUE 1 #endif