From 36c0bad1fa4d61860a9cb4688759a49fa22cde67 Mon Sep 17 00:00:00 2001 From: Olivier Blin Date: Wed, 28 Sep 2005 12:52:36 +0000 Subject: Initial revision --- COPYING | 340 ++++++++++++++++++++++++++++++ Makefile | 18 ++ conf/mandi.conf | 36 ++++ mandi.spec | 152 ++++++++++++++ rules.d/psd | 1 + scripts/mandi.init | 45 ++++ scripts/start | 5 + scripts/stop | 3 + src/Makefile | 25 +++ src/mandi_daemon.c | 269 ++++++++++++++++++++++++ src/plugin.h | 21 ++ src/plugins/ifw/black_list.c | 76 +++++++ src/plugins/ifw/black_list.h | 20 ++ src/plugins/ifw/ifw.h | 48 +++++ src/plugins/ifw/ifw_dbus.c | 456 ++++++++++++++++++++++++++++++++++++++++ src/plugins/ifw/ifw_dbus.h | 43 ++++ src/plugins/ifw/ipset.c | 89 ++++++++ src/plugins/ifw/ipset.h | 22 ++ src/plugins/ifw/libnl_ifw.c | 62 ++++++ src/plugins/ifw/libnl_ifw.h | 35 +++ src/plugins/ifw/list.h | 155 ++++++++++++++ src/plugins/ifw/plugin.c | 224 ++++++++++++++++++++ src/plugins/ifw/report_list.c | 81 +++++++ src/plugins/ifw/report_list.h | 24 +++ src/plugins/ifw/white_list.c | 119 +++++++++++ src/plugins/ifw/white_list.h | 23 ++ src/plugins/wireless/plugin.c | 148 +++++++++++++ src/plugins/wireless/wpa_ctrl.c | 238 +++++++++++++++++++++ src/plugins/wireless/wpa_ctrl.h | 179 ++++++++++++++++ 29 files changed, 2957 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 conf/mandi.conf create mode 100644 mandi.spec create mode 100644 rules.d/psd create mode 100644 scripts/mandi.init create mode 100644 scripts/start create mode 100644 scripts/stop create mode 100644 src/Makefile create mode 100644 src/mandi_daemon.c create mode 100644 src/plugin.h create mode 100644 src/plugins/ifw/black_list.c create mode 100644 src/plugins/ifw/black_list.h create mode 100644 src/plugins/ifw/ifw.h create mode 100644 src/plugins/ifw/ifw_dbus.c create mode 100644 src/plugins/ifw/ifw_dbus.h create mode 100644 src/plugins/ifw/ipset.c create mode 100644 src/plugins/ifw/ipset.h create mode 100644 src/plugins/ifw/libnl_ifw.c create mode 100644 src/plugins/ifw/libnl_ifw.h create mode 100644 src/plugins/ifw/list.h create mode 100644 src/plugins/ifw/plugin.c create mode 100644 src/plugins/ifw/report_list.c create mode 100644 src/plugins/ifw/report_list.h create mode 100644 src/plugins/ifw/white_list.c create mode 100644 src/plugins/ifw/white_list.h create mode 100644 src/plugins/wireless/plugin.c create mode 100644 src/plugins/wireless/wpa_ctrl.c create mode 100644 src/plugins/wireless/wpa_ctrl.h diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2f30e1c --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +PACKAGE = mandi +VERSION := $(shell rpm -q --qf '%{VERSION}\n' --specfile $(PACKAGE).spec | head -1) +RELEASE := $(shell rpm -q --qf '%{RELEASE}\n' --specfile $(PACKAGE).spec | head -1) + +all: + (cd src; make) + +clean: + find -maxdepth 1 -type d -name 'mandi-*' -exec rm -r {} \; + find -name '*~' -exec rm {} \; + make -C src clean + +dist: clean + mkdir ../$(PACKAGE)-$(VERSION) + tar c . | tar x -C ../$(PACKAGE)-$(VERSION) + tar cvjf ../mandi-$(VERSION).tar.bz2 ../mandi-$(VERSION) + rm -rf ../mandi-$(VERSION) + rpm -ts ../mandi-$(VERSION).tar.bz2 diff --git a/conf/mandi.conf b/conf/mandi.conf new file mode 100644 index 0000000..0ae9f8e --- /dev/null +++ b/conf/mandi.conf @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mandi.spec b/mandi.spec new file mode 100644 index 0000000..a96815b --- /dev/null +++ b/mandi.spec @@ -0,0 +1,152 @@ +%define name mandi +%define version 0.7.2 +%define release 2mdk + +Summary: Monitoring daemon bridge +Name: %{name} +Version: %{version} +Release: %{release} +Source0: %{name}-%{version}.tar.bz2 +License: GPL +Group: Networking/Other +Url: http://cvs.mandriva.com/cgi-bin/cvsweb.cgi/soft/ +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +BuildRequires: dbus-devel +Requires(post): rpm-helper +Requires: dbus + +%description +Mandi is a monitoring daemon which acts as a bridge from root +monitoring libraries to user applications, using D-Bus. +Its plugin system allows to monitor different kind of events. +A built-in plugin forwards wireless scan results from wpa_supplicant +to user applications. + +Mandi ain't no daemon, idiot! (c) rgs + +%package ifw +Summary: Firewall rules for Interactive Firewall +Group: Networking/Other +Requires(post): shorewall +Requires: mandi = %{version} +Requires: ipset + +%description ifw +This package contains the iptables rules used to forward intrusion +detections to the mandi daemon. +It is a component of Interactive Firewall. + +%prep +%setup -q + +%build +%make + +%install +rm -rf $RPM_BUILD_ROOT +install -D -m755 src/%{name} $RPM_BUILD_ROOT%{_sbindir}/%{name} +install -D -m644 conf/%{name}.conf $RPM_BUILD_ROOT%{_sysconfdir}/dbus-1/system.d/%{name}.conf +install -D -m755 scripts/%{name}.init %buildroot%{_initrddir}/%{name} +install -d -m755 %buildroot%{_sysconfdir}/ifw/rules.d/ +install -m644 rules.d/* %buildroot%{_sysconfdir}/ifw/rules.d/ +install -m644 scripts/{start,stop} $RPM_BUILD_ROOT%{_sysconfdir}/ifw + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +%_post_service mandi + +%preun +%_preun_service mandi + +%triggerpostun ifw -- mandi-ifw < 0.7.2-2mdk +STARTFILE=%{_sysconfdir}/shorewall/start +if [ -f $STARTFILE ]; then + grep -v -E "^INCLUDE ifw$" $STARTFILE > $STARTFILE.new + mv -f $STARTFILE.new $STARTFILE +fi + +%files +%defattr(-,root,root) +%{_sbindir}/%{name} +%config %{_sysconfdir}/dbus-1/system.d/%{name}.conf +%{_initrddir}/mandi + +%files ifw +%dir %{_sysconfdir}/ifw/ +%{_sysconfdir}/ifw/start +%{_sysconfdir}/ifw/stop +%{_sysconfdir}/ifw/rules.d/* + +%changelog +* Sun Sep 4 2005 Olivier Blin 0.7.2-2mdk +- remove "INCLUDE ifw" in shorewall start file on upgrade + +* Thu Sep 1 2005 Olivier Blin 0.7.2-1mdk +- mandi: save white list right after adding/modifying entries + +* Thu Sep 1 2005 Olivier Blin 0.7.1-1mdk +- 0.7.1: + o add start/stop scripts + o add rules.d directorty, with a sample psd rule + o shorewall isn't required by the package anymore + o remove shorewall workarounds, it'is better done by drakfirewall now + o notify attacks even if attacker is already present in log + o don't create ipsets in the daemon + +* Thu Aug 25 2005 Olivier Blin 0.7-2mdk +- use clean tarball (fix build on 64 bits, thanks couriousous) + +* Wed Aug 24 2005 Olivier Blin 0.7-1mdk +- 0.7, IFW plugin improvements: + o keep logs and allow to clear them + o allow applications to notify themselves when the user has + checked reports or asked to manage the lists + o send notifications in automatic mode too + +* Mon Aug 22 2005 Olivier Blin 0.6-4mdk +- do not match for ESTABLISHED,RELATED connections (Samir), + this should avoid DNS blacklist + +* Mon Aug 22 2005 Olivier Blin 0.6-3mdk +- remove ifw inclusion in shorewall on full removal only + +* Sat Aug 20 2005 Olivier Blin 0.6-2mdk +- really fix dbus permissions + +* Fri Aug 19 2005 Olivier Blin 0.6-1mdk +- 0.6 + o create ipsets in shorewall start script + o start mandi service after messagebus + o allow console users to use Interactive Firewall + +* Thu Aug 18 2005 Olivier Blin 0.5-1mdk +- 0.5 + o use an Ifw chain in shorewall/iptables + o handle blacklist and whitelist in the Ifw chain + +* Thu Aug 18 2005 Olivier Blin 0.4-1mdk +- 0.4 (ignore notifications from the loopback interface) +- start mandi daemon as a service +- add a mandi-ifw subpackage to insert Interactive Firewall in + shorewall start rules +- use psd to detect port scans + +* Thu Aug 11 2005 Olivier Blin 0.3-1mdk +- 0.3, Interactive Firewall improvements: + o really support ipset (using iptrees) + o use correct byte order to add IP addresses in iptrees + o send only one attack report per IP address + o fix description (thanks to Mathieu Geli) + +* Fri Jul 29 2005 Olivier Blin 0.2-1mdk +- 0.2 (small bugfix) +- Requires ipset + +* Thu Jul 28 2005 Olivier Blin 0.1-1mdk +- allow to select a wireless network +- enable fake Active Firewall mode + +* Fri Jul 15 2005 Olivier Blin 0.1-0.1mdk +- initial release diff --git a/rules.d/psd b/rules.d/psd new file mode 100644 index 0000000..c482764 --- /dev/null +++ b/rules.d/psd @@ -0,0 +1 @@ +iptables -A Ifw -m state --state NEW,INVALID -m psd --psd-weight-threshold 10 --psd-delay-threshold 10000 --psd-lo-ports-weight 1 --psd-hi-ports-weight 2 -j IFWLOG --log-prefix SCAN diff --git a/scripts/mandi.init b/scripts/mandi.init new file mode 100644 index 0000000..1fa205c --- /dev/null +++ b/scripts/mandi.init @@ -0,0 +1,45 @@ +#!/bin/sh +# +# Startup script for the mandi daemon (oblin@mandriva.com) +# +# chkconfig: 2345 26 59 +# +# description: Network monitoring daemon (Interactive Firewall and wireless) +# +# + +. /etc/init.d/functions + +case "$1" in + start) + gprintf "Starting mandi daemon: " + daemon /usr/sbin/mandi -d + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/mandi + ;; + stop) + gprintf "Shutting down mandi daemon: " + killproc mandi + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/mandi + ;; + status) + status mandi + RETVAL=$? + ;; + restart|reload) + $0 stop + $0 start + ;; + condrestart) + [ -f /var/lock/subsys/mandi ] && restart || : + ;; + *) + gprintf "Usage: %s {start|stop|status|restart}\n" "$0" + RETVAL=1 + ;; +esac + +exit $RETVAL diff --git a/scripts/start b/scripts/start new file mode 100644 index 0000000..b416e8b --- /dev/null +++ b/scripts/start @@ -0,0 +1,5 @@ +iptables -N Ifw +ipset -N ifw_wl iptree +iptables -A Ifw -m set --set ifw_wl src -j RETURN +ipset -N ifw_bl iptree --timeout 3600 +iptables -A Ifw -m set --set ifw_bl src -j DROP diff --git a/scripts/stop b/scripts/stop new file mode 100644 index 0000000..1e9af67 --- /dev/null +++ b/scripts/stop @@ -0,0 +1,3 @@ +ipset -X ifw_bl +ipset -X ifw_wl +iptables -X Ifw diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ba8b1c8 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,25 @@ +CC = gcc +CFLAGS = -Wall -g +DEFS = -DDBUS_API_SUBJECT_TO_CHANGE=1 +INCLUDES = $(shell pkg-config dbus-1 --cflags) -I$(PWD) +LDFLAGS = $(shell pkg-config dbus-1 --libs) + +DAEMON_LDFLAGS = +DAEMON_OBJS = mandi_daemon.o +DAEMON_TARGET = mandi + +DAEMON_OBJS += $(addprefix plugins/wireless/,plugin.o wpa_ctrl.o) + +#CFLAGS += -DIFW_FAKE +DAEMON_OBJS += $(addprefix plugins/ifw/,plugin.o ifw_dbus.o ipset.o white_list.o black_list.o report_list.o libnl_ifw.o) + +all: $(DAEMON_TARGET) + +.c.o: + $(CC) $(CFLAGS) $(DEFS) $(INCLUDES) -c $< -o $@ + +$(DAEMON_TARGET): $(DAEMON_OBJS) + $(CC) $(CFLAGS) $(DAEMON_OBJS) $(LDFLAGS) $(DAEMON_LDFLAGS) -o $@ + +clean: + rm -f $(DAEMON_OBJS) $(DAEMON_TARGET) diff --git a/src/mandi_daemon.c b/src/mandi_daemon.c new file mode 100644 index 0000000..6c67d88 --- /dev/null +++ b/src/mandi_daemon.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "plugin.h" + +#define MANDI_DAEMON_SERVICE PLUGIN_ROOT_INTF + +typedef struct { + DBusConnection *bus; + DBusWatch *bus_read_watch; + int bus_read_fd; +} mandi_daemon_t; + +extern plugin_t wpa_supplicant_plugin; +extern plugin_t ifw_plugin; +plugin_t *plugins[] = { + &wpa_supplicant_plugin, + &ifw_plugin, + NULL, +}; + +static mandi_daemon_t *mandi_daemon_p; /* global variable used in signal handlers only */ +static void sigquit(int signum); +static int mandi_daemon_init(mandi_daemon_t *daemon); +static void mandi_daemon_exit(mandi_daemon_t *daemon, int exit_code); +static void mandi_daemon_handle_dbus(mandi_daemon_t *daemon); +static int mandi_daemon_acquire_service(mandi_daemon_t *daemon); +static int mandi_daemon_init_watch(mandi_daemon_t *daemon); +static dbus_bool_t mandi_daemon_add_watch(DBusWatch *watch, void *data); +static void mandi_daemon_toggle_watch(DBusWatch *watch, void *data); +static void mandi_daemon_remove_watch(DBusWatch *watch, void *data); +static DBusConnection *mandi_daemon_get_system_bus(); +static int mandi_daemon_init_path(mandi_daemon_t *daemon, plugin_t *plugin); +static void mandi_daemon_object_path_unregister(DBusConnection *connection, void *user_data); +static DBusHandlerResult mandi_daemon_object_path_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data); + +int main(int argc, char **argv) +{ + mandi_daemon_t mandi_daemon; + + /* set up signal handlers to exit nicely when needed */ + mandi_daemon_p = &mandi_daemon; + signal(SIGINT, sigquit); + signal(SIGTERM, sigquit); + signal(SIGQUIT, sigquit); + + mandi_daemon_init(&mandi_daemon); + + if (getopt(argc, argv, "d") == 'd') { + daemon(0, 0); + } + + printf("Monitoring daemon waiting for events ...\n"); + + while (1) { + int num_fds; + fd_set read_fds; + plugin_t **ptr; + plugin_t *plugin; + + FD_ZERO(&read_fds); + FD_SET(mandi_daemon.bus_read_fd, &read_fds); + num_fds = mandi_daemon.bus_read_fd + 1; + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + if (plugin->fd >= 0) { + FD_SET(plugin->fd, &read_fds); + if (plugin->fd + 1 > num_fds) { + num_fds = plugin->fd + 1; + } + } + } + + if (select(num_fds, &read_fds, NULL, NULL, NULL) > 0) { + if (FD_ISSET(mandi_daemon.bus_read_fd, &read_fds)) { + mandi_daemon_handle_dbus(&mandi_daemon); + } + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + if (plugin->fd >= 0 && FD_ISSET(plugin->fd, &read_fds)) { + plugin->handle_incoming(plugin, mandi_daemon.bus); + } + } + } else { + fprintf(stderr, "unhandled error in select(): %s\n", strerror(errno)); + + } + } + + mandi_daemon_exit(&mandi_daemon, EXIT_SUCCESS); + return 0; +} + +static void sigquit(int signum) { + printf("SIGINT, SIGTERM or SIGQUIT catched, trying to exit nicely\n"); + mandi_daemon_exit(mandi_daemon_p, EXIT_SUCCESS); +} + +static int mandi_daemon_init(mandi_daemon_t *daemon) { + plugin_t **ptr; + plugin_t *plugin; + + daemon->bus = mandi_daemon_get_system_bus(); + if (!daemon->bus) { + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + if (mandi_daemon_init_watch(daemon) != 0) { + fprintf(stderr, "unable to init DBus watch\n"); + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + if (mandi_daemon_acquire_service(daemon) != 0) { + fprintf(stderr, "unable to init DBus service\n"); + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + if (plugin->init(plugin, daemon->bus) != 0) { + fprintf(stderr, "unable to init \"%s\" plugin\n", plugin->name); + mandi_daemon_exit(daemon, EXIT_FAILURE); + } + + mandi_daemon_init_path(daemon, plugin); + } + + return 0; +} + +static void mandi_daemon_exit(mandi_daemon_t *daemon, int exit_code) { + plugin_t **ptr; + plugin_t *plugin; + for (ptr = plugins; *ptr; ptr++) { + plugin = *ptr; + plugin->deinit(plugin, daemon->bus); + } + /* unregister dbus */ + exit(exit_code); +} + +static void mandi_daemon_handle_dbus(mandi_daemon_t *daemon) { + dbus_watch_handle(daemon->bus_read_watch, DBUS_WATCH_READABLE); + dbus_connection_dispatch(daemon->bus); +} + +int mandi_daemon_acquire_service(mandi_daemon_t *daemon) { + DBusError error; + dbus_error_init(&error); + + if (dbus_bus_acquire_service(daemon->bus, MANDI_DAEMON_SERVICE, 0, &error) == -1) { + fprintf(stderr, "dbus_bus_acquire_service(): %s\n", error.message); + fprintf(stderr, "Make sure a DBus policy allows you to acquire this service.\n"); + dbus_error_free(&error); + return -1; + } + dbus_connection_dispatch(daemon->bus); + + dbus_error_free(&error); + return 0; +} + +/* get fds to be watched */ +static int mandi_daemon_init_watch(mandi_daemon_t *daemon) { + DBusError error; + + dbus_error_init(&error); + + if (dbus_connection_set_watch_functions(daemon->bus, + mandi_daemon_add_watch, + mandi_daemon_toggle_watch, + mandi_daemon_remove_watch, + (void *) daemon, + NULL) == FALSE) { + fprintf(stderr, "dbus_connection_set_watch_functions(): %s\n", error.message); + dbus_error_free(&error); + return -1; + } + dbus_connection_dispatch(daemon->bus); + + dbus_error_free(&error); + return 0; +} + +static dbus_bool_t mandi_daemon_add_watch(DBusWatch *watch, void *data) { + mandi_daemon_t *daemon = (mandi_daemon_t *) data; + if (dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE) { + fprintf(stderr, "mandi_daemon_add_watch(): READABLE\n"); + daemon->bus_read_watch = watch; + daemon->bus_read_fd = dbus_watch_get_fd(daemon->bus_read_watch); + } + /* do nothing for WRITABLE watch, we dispatch when needed */ + return TRUE; +} + +static void mandi_daemon_toggle_watch(DBusWatch *watch, void *data) { + fprintf(stderr, "mandi_daemon_toggle_watch()\n"); + /* FIXME : do we need to do something here ? */ +} + +static void mandi_daemon_remove_watch(DBusWatch *watch, void *data) { + if (dbus_watch_get_flags(watch) & DBUS_WATCH_READABLE) { + fprintf(stderr, "mandi_daemon_remove_watch(): READABLE\n"); + /* FIXME : do we need to do something here ? */ + } +} + +/* set a connection to the system bus */ +static DBusConnection *mandi_daemon_get_system_bus() { + DBusConnection *bus; + DBusError error; + dbus_error_init(&error); + + bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (!bus) { + fprintf(stderr, "Failed to connect to the D-BUS daemon: %s\n", error.message); + dbus_error_free(&error); + return NULL; + } + + dbus_error_free(&error); + return bus; +} + +static int mandi_daemon_init_path(mandi_daemon_t *daemon, plugin_t *plugin) { + struct DBusObjectPathVTable object_path_vtable = { + mandi_daemon_object_path_unregister, + mandi_daemon_object_path_handle_message, + NULL, NULL, NULL, NULL + }; + + DBusError error; + dbus_error_init(&error); + + if (dbus_connection_register_object_path(daemon->bus, plugin->path, &object_path_vtable, plugin) == FALSE) { + fprintf(stderr, "dbus_connection_register_object_path(): not enough memory\n"); + dbus_error_free(&error); + return -1; + } + dbus_connection_dispatch(daemon->bus); + + dbus_error_free(&error); + return 0; +} + +static void mandi_daemon_object_path_unregister(DBusConnection *connection, void *user_data) { +} + +static DBusHandlerResult mandi_daemon_object_path_handle_message(DBusConnection *connection, DBusMessage *message, void *user_data) { + plugin_t *plugin = (plugin_t *) user_data; + + printf("handling method call '%s' on interface '%s'\n", + dbus_message_get_member(message), + dbus_message_get_interface(message)); + + return plugin->handle_message(connection, message, plugin); +} diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000..ad3973a --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,21 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include + +#define PLUGIN_ROOT_INTF "com.mandriva.monitoring" +#define PLUGIN_ROOT_PATH "/com/mandriva/monitoring" + +typedef struct plugin_s plugin_t; +struct plugin_s { + const char *name; + const char *path; + int fd; + void *priv; + int (*init)(plugin_t *plugin, DBusConnection *connection); + void (*handle_incoming)(plugin_t *plugin, DBusConnection *connection); + DBusHandlerResult (*handle_message)(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); + void (*deinit)(plugin_t *plugin, DBusConnection *connection); +}; + +#endif /* PLUGIN_H */ diff --git a/src/plugins/ifw/black_list.c b/src/plugins/ifw/black_list.c new file mode 100644 index 0000000..1e7dbbb --- /dev/null +++ b/src/plugins/ifw/black_list.c @@ -0,0 +1,76 @@ +#include "black_list.h" +#include "ipset.h" + +#include +#include +#include +#include +#include + +void black_list_init(black_list_t *list) { + INIT_LIST_HEAD(list); +} + +void black_list_add(black_list_t *list, msg_usr_t *attack) { + black_list_cell_t *cell; + + cell = malloc(sizeof(black_list_cell_t)); + if (!cell) { + fprintf(stderr, "unable to alloc enough memory for black list cell, skipping\n"); + return; + } + cell->info = *attack; + INIT_LIST_HEAD(&cell->list); + list_add_tail(&cell->list, list); + + ipset_blacklist_add(cell->info.s_addr); +} + +black_list_cell_t *black_list_find(black_list_t *list, u_int32_t addr) { + struct list_head *entry; + + __list_for_each(entry, list) { + black_list_cell_t *cell; + cell = list_entry(entry, black_list_cell_t, list); + if (cell->info.s_addr == addr) { + return cell; + } + } + + return NULL; +} + +void black_list_remove(black_list_t *list, u_int32_t addr) { + black_list_cell_t *cell, *n, *prev; + + ipset_blacklist_remove(addr); + + prev = NULL; + list_for_each_entry_safe(cell, n, list, list) { + if (prev) + free(prev); + if (cell->info.s_addr == addr) { + list_del(&cell->list); + prev = cell; + } else { + prev = NULL; + } + } + if (prev) + free(prev); +} + + +void black_list_print(black_list_t *list) { + struct list_head *entry; + + printf("* black list {\n"); + __list_for_each(entry, list) { + black_list_cell_t *cell; + struct in_addr addr; + cell = list_entry(entry, black_list_cell_t, list); + addr.s_addr = cell->info.s_addr; + printf("%s,\n", inet_ntoa(addr)); + } + printf("} black list *\n"); +} diff --git a/src/plugins/ifw/black_list.h b/src/plugins/ifw/black_list.h new file mode 100644 index 0000000..4cfe74b --- /dev/null +++ b/src/plugins/ifw/black_list.h @@ -0,0 +1,20 @@ +#ifndef BLACK_LIST_H +#define BLACK_LIST_H + +#include "list.h" +typedef struct list_head black_list_t; + +#include "ifw.h" + +typedef struct { + struct list_head list; + msg_usr_t info; +} black_list_cell_t; + +void black_list_init(black_list_t *list); +void black_list_add(black_list_t *list, msg_usr_t *attack); +black_list_cell_t *black_list_find(black_list_t *list, u_int32_t addr); +void black_list_remove(black_list_t *list, u_int32_t addr); +void black_list_print(black_list_t *list); + +#endif /* BLACK_LIST_H */ diff --git a/src/plugins/ifw/ifw.h b/src/plugins/ifw/ifw.h new file mode 100644 index 0000000..f007b0f --- /dev/null +++ b/src/plugins/ifw/ifw.h @@ -0,0 +1,48 @@ +#ifndef IFW_H +#define IFW_H + +#include "plugin.h" + +#define IFW_DBUS_PATH PLUGIN_ROOT_PATH "/ifw" +#define IFW_DBUS_INTERFACE PLUGIN_ROOT_INTF ".ifw" + +#include +#include "libnl_ifw.h" + +#define IFW_SYSCONF_ROOT "/etc/ifw/" +#define IFW_BLACKLIST_FILENAME IFW_SYSCONF_ROOT "blacklist" +#define IFW_WHITELIST_FILENAME IFW_SYSCONF_ROOT "whitelist" + +typedef enum { + IFW_MODE_AUTO, + IFW_MODE_INTERACTIVE +} ifw_mode_t; + +typedef struct { + long timestamp_sec; /* date */ + char indev_name[IFNAMSIZ]; /* input interface */ + char prefix[PREFSIZ]; /* summary of attack */ + int sensor; /* sensor the alert come from */ + int protocol; /* Protocol */ + u_int32_t s_addr; /* source address */ + u_int16_t d_port; /* destination port UDP/TCP */ + u_int8_t icmp_type; /* icmp type */ +} msg_usr_t; + +typedef struct popup_verdict { + int seq; + int bl; +} popup_verdict_t; + +#include "black_list.h" +#include "white_list.h" +#include "report_list.h" + +typedef struct { + ifw_mode_t mode; + black_list_t blacklist; + report_list_t reports; + white_list_t whitelist; +} ifw_t; + +#endif /* IFW_H */ diff --git a/src/plugins/ifw/ifw_dbus.c b/src/plugins/ifw/ifw_dbus.c new file mode 100644 index 0000000..82b2675 --- /dev/null +++ b/src/plugins/ifw/ifw_dbus.c @@ -0,0 +1,456 @@ +#include + +#include "ifw_dbus.h" + +static void ifw_dbus_notify_simple_signal(DBusConnection *bus, char *signal) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + signal); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +void ifw_dbus_apply_report_verdict(DBusConnection *connection, ifw_t *ifw, report_list_cell_t *report, int do_blacklist) { + if (do_blacklist) { + if (!black_list_find(&ifw->blacklist, report->info.s_addr)) { + printf("blacklisting seq %d\n", report->seq); + black_list_add(&ifw->blacklist, &report->info); + ifw_dbus_notify_blacklist(connection, &report->info); + } else { + printf("(seq %d) addr %u already in blacklist\n", report->seq, report->info.s_addr); + } + } else { + printf("ignoring seq %d\n", report->seq); + } + report->processed = 1; +} + +/* notify frontends of a new attack with a DBus signal */ +void ifw_dbus_notify_attack(DBusConnection *bus, report_list_cell_t *report) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + "Attack"); + + dbus_message_append_args(message, + DBUS_TYPE_UINT32, + report->info.timestamp_sec, + DBUS_TYPE_STRING, + report->info.indev_name, + DBUS_TYPE_STRING, + report->info.prefix, + DBUS_TYPE_UINT32, + report->info.sensor, + DBUS_TYPE_UINT32, + report->info.protocol, + DBUS_TYPE_UINT32, + report->info.s_addr, + DBUS_TYPE_UINT32, + report->info.d_port, + DBUS_TYPE_UINT32, + report->info.icmp_type, + DBUS_TYPE_UINT32, + report->seq, + DBUS_TYPE_UINT32, + report->processed, + DBUS_TYPE_INVALID); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +/* notify frontends of a new blacklist with a DBus signal */ +void ifw_dbus_notify_blacklist(DBusConnection *bus, msg_usr_t *attack) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + "Blacklist"); + + dbus_message_append_args(message, + DBUS_TYPE_UINT32, + attack->timestamp_sec, + DBUS_TYPE_STRING, + attack->indev_name, + DBUS_TYPE_STRING, + attack->prefix, + DBUS_TYPE_UINT32, + attack->sensor, + DBUS_TYPE_UINT32, + attack->protocol, + DBUS_TYPE_UINT32, + attack->s_addr, + DBUS_TYPE_UINT32, + attack->d_port, + DBUS_TYPE_UINT32, + attack->icmp_type, + DBUS_TYPE_INVALID); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +/* notify frontends of a new whitelist with a DBus signal */ +void ifw_dbus_notify_whitelist(DBusConnection *bus, u_int32_t addr) { + DBusMessage *message; + + message = dbus_message_new_signal(IFW_DBUS_PATH, + IFW_DBUS_INTERFACE, + "Whitelist"); + dbus_message_append_args(message, + DBUS_TYPE_UINT32, + addr, + DBUS_TYPE_INVALID); + dbus_connection_send(bus, message, NULL); + dbus_connection_flush(bus); + dbus_message_unref(message); +} + +/* notify frontends that ifw data isn't usable with a DBus signal */ +void ifw_dbus_notify_clear(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "Clear"); +} + +/* notify frontends that ifw has just been started */ +void ifw_dbus_notify_init(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "Init"); +} + +/* notify frontends that a user is aware of the attacks */ +void ifw_dbus_notify_alert_ack(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "AlertAck"); +} + +/* notify frontends that a user is wants to review the attacks */ +void ifw_dbus_notify_manage_request(DBusConnection *bus) { + ifw_dbus_notify_simple_signal(bus, "ManageRequest"); +} + +DBusHandlerResult ifw_dbus_get_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + ifw->mode, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_set_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + ifw_mode_t mode; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &mode, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_set_mode(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + printf("setting new ifw mode : %s\n", mode == IFW_MODE_AUTO ? "auto" : mode == IFW_MODE_INTERACTIVE ? "interactive" : "unknown"); + ifw->mode = mode; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + ifw->mode, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_get_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + struct list_head *entry; + DBusMessage *reply; + char include_processed; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &include_processed, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_get_reports(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + reply = dbus_message_new_method_return(message); + __list_for_each(entry, &ifw->reports) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + if (cell->processed && !include_processed) { + continue; + } + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + cell->info.timestamp_sec, + DBUS_TYPE_STRING, + cell->info.indev_name, + DBUS_TYPE_STRING, + cell->info.prefix, + DBUS_TYPE_UINT32, + cell->info.sensor, + DBUS_TYPE_UINT32, + cell->info.protocol, + DBUS_TYPE_UINT32, + cell->info.s_addr, + DBUS_TYPE_UINT32, + cell->info.d_port, + DBUS_TYPE_UINT32, + cell->info.icmp_type, + DBUS_TYPE_UINT32, + cell->seq, + DBUS_TYPE_UINT32, + cell->processed, + DBUS_TYPE_INVALID); + } + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_get_blacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + struct list_head *entry; + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + __list_for_each(entry, &ifw->blacklist) { + black_list_cell_t *cell; + cell = list_entry(entry, black_list_cell_t, list); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + cell->info.timestamp_sec, + DBUS_TYPE_STRING, + cell->info.indev_name, + DBUS_TYPE_STRING, + cell->info.prefix, + DBUS_TYPE_UINT32, + cell->info.sensor, + DBUS_TYPE_UINT32, + cell->info.protocol, + DBUS_TYPE_UINT32, + cell->info.s_addr, + DBUS_TYPE_UINT32, + cell->info.d_port, + DBUS_TYPE_UINT32, + cell->info.icmp_type, + DBUS_TYPE_INVALID); + } + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_set_blacklist_verdict(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + int seq, do_blacklist; + report_list_cell_t *report; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &seq, + DBUS_TYPE_UINT32, + &do_blacklist, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_blacklist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + report = report_list_find_seq(&ifw->reports, seq); + if (report) { + ifw_dbus_apply_report_verdict(connection, ifw, report, do_blacklist); + } else { + fprintf(stderr, "unable find sequence number in report list, skipping\n"); + } + + black_list_print(&ifw->blacklist); + report_list_print(&ifw->reports); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_unblacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + u_int32_t addr; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &addr, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_blacklist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + black_list_remove(&ifw->blacklist, addr); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_get_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + struct list_head *entry; + DBusMessage *reply; + + reply = dbus_message_new_method_return(message); + __list_for_each(entry, &ifw->whitelist) { + white_list_cell_t *cell; + cell = list_entry(entry, white_list_cell_t, list); + dbus_message_append_args(reply, + DBUS_TYPE_UINT32, + cell->addr, + DBUS_TYPE_INVALID); + } + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + u_int32_t addr; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &addr, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_whitelist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + if (!white_list_find(&ifw->whitelist, addr)) { + printf("whitelisting addr %u\n", addr); + white_list_add(&ifw->whitelist, addr); + } else { + printf("addr %u already in whitelist\n", addr); + } + white_list_print(&ifw->whitelist); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + ifw_dbus_notify_whitelist(connection, addr); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_unwhitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusError error; + DBusMessage *reply; + u_int32_t addr; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &addr, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "ifw_dbus_whitelist(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + printf("remove addr from whitelist %u\n", addr); + white_list_remove(&ifw->whitelist, addr); + + white_list_print(&ifw->whitelist); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_clear_processed_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + report_list_clear_processed(&ifw->reports); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_send_alert_ack(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + ifw_dbus_notify_alert_ack(connection); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +DBusHandlerResult ifw_dbus_send_manage_request(DBusConnection *connection, DBusMessage *message, ifw_t *ifw) { + DBusMessage *reply; + + ifw_dbus_notify_manage_request(connection); + + reply = dbus_message_new_method_return(message); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} diff --git a/src/plugins/ifw/ifw_dbus.h b/src/plugins/ifw/ifw_dbus.h new file mode 100644 index 0000000..a4385db --- /dev/null +++ b/src/plugins/ifw/ifw_dbus.h @@ -0,0 +1,43 @@ +#ifndef IFW_DBUS_H +#define IFW_DBUS_H + +#include +#include "ifw.h" + +void ifw_dbus_apply_report_verdict(DBusConnection *connection, ifw_t *ifw, report_list_cell_t *report, int do_blacklist); + +/* notify frontends of a new attack with a DBus signal */ +void ifw_dbus_notify_attack(DBusConnection *bus, report_list_cell_t *report); + +/* notify frontends of a new whitelist with a DBus signal */ +void ifw_dbus_notify_whitelist(DBusConnection *bus, u_int32_t addr); + +/* notify frontends of a new blacklist with a DBus signal */ +void ifw_dbus_notify_blacklist(DBusConnection *bus, msg_usr_t *attack); + +/* notify frontends that ifw data isn't usable with a DBus signal */ +void ifw_dbus_notify_clear(DBusConnection *bus); + +/* notify frontends that ifw has just been started */ +void ifw_dbus_notify_init(DBusConnection *bus); + +/* notify frontends that a user is aware of the attacks */ +void ifw_dbus_notify_alert_ack(DBusConnection *bus); + +/* notify frontends that a user is wants to review the attacks */ +void ifw_dbus_notify_manage_request(DBusConnection *bus); + +DBusHandlerResult ifw_dbus_get_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_set_mode(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_get_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_get_blacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_set_blacklist_verdict(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_unblacklist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_get_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_whitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_unwhitelist(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_clear_processed_reports(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_send_alert_ack(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); +DBusHandlerResult ifw_dbus_send_manage_request(DBusConnection *connection, DBusMessage *message, ifw_t *ifw); + +#endif /* IFW_DBUS_H */ diff --git a/src/plugins/ifw/ipset.c b/src/plugins/ifw/ipset.c new file mode 100644 index 0000000..74ca06e --- /dev/null +++ b/src/plugins/ifw/ipset.c @@ -0,0 +1,89 @@ +#include "ipset.h" + +#include +#include +#include + +#define CMD_MAX_SIZE 1024 + +#ifdef IPSET_DEBUG +#define DPRINTF(s) printf("%s\n", s) +#else +#define DPRINTF(s) +#endif + +void ipset_init() { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -N " IPSET_BLACKLIST_NAME " iptree --timeout " IPSET_BLACKLIST_TIMEOUT); + DPRINTF(cmd); + system(cmd); + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -N " IPSET_WHITELIST_NAME " iptree"); + DPRINTF(cmd); + system(cmd); +} + +void ipset_destroy() { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -X " IPSET_BLACKLIST_NAME); + DPRINTF(cmd); + system(cmd); + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -X " IPSET_WHITELIST_NAME); + DPRINTF(cmd); + system(cmd); +} + +/* void ipset_blacklist_load(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -R < %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +/* void ipset_blacklist_save(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -S " IPSET_BLACKLIST_NAME " > %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +/* void ipset_whitelist_load(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -R < %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +/* void ipset_whitelist_save(char *filename) { */ +/* char cmd[CMD_MAX_SIZE]; */ +/* snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -S " IPSET_WHITELIST_NAME " > %s", filename); */ +/* DPRINTF(cmd); */ +/* system(cmd); */ +/* } */ + +void ipset_blacklist_add(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -A " IPSET_BLACKLIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} + +void ipset_blacklist_remove(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -D " IPSET_BLACKLIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} + +void ipset_whitelist_add(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -A " IPSET_WHITELIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} + +void ipset_whitelist_remove(u_int32_t addr) { + char cmd[CMD_MAX_SIZE]; + snprintf(cmd, CMD_MAX_SIZE, IPSET_CMD " -D " IPSET_WHITELIST_NAME " %u", ntohl(addr)); + DPRINTF(cmd); + system(cmd); +} diff --git a/src/plugins/ifw/ipset.h b/src/plugins/ifw/ipset.h new file mode 100644 index 0000000..a78395a --- /dev/null +++ b/src/plugins/ifw/ipset.h @@ -0,0 +1,22 @@ +#ifndef IPSET_H +#define IPSET_H + +#define IPSET_CMD "ipset" +#define IPSET_BLACKLIST_NAME "ifw_bl" +#define IPSET_WHITELIST_NAME "ifw_wl" +#define IPSET_BLACKLIST_TIMEOUT "3600" + +#include + +void ipset_init(); +void ipset_destroy(); +/* void ipset_blacklist_load(char *filename); */ +/* void ipset_blacklist_save(char *filename); */ +/* void ipset_whitelist_load(char *filename); */ +/* void ipset_whitelist_save(char *filename); */ +void ipset_blacklist_add(u_int32_t addr); +void ipset_blacklist_remove(u_int32_t addr); +void ipset_whitelist_add(u_int32_t addr); +void ipset_whitelist_remove(u_int32_t addr); + +#endif /* IPSET_H */ diff --git a/src/plugins/ifw/libnl_ifw.c b/src/plugins/ifw/libnl_ifw.c new file mode 100644 index 0000000..bb41363 --- /dev/null +++ b/src/plugins/ifw/libnl_ifw.c @@ -0,0 +1,62 @@ +/* nl_create_socket(), nl_bind_socket() and nl_read_msg() + * for Interactive Firewall + * sbellabes@mandriva.com + */ + + +#include +#include +#include +#include +#include +#include + +#include "libnl_ifw.h" + +int nl_ifw_bind_socket(int s) { + struct sockaddr_nl saddr_nl; + int res; + + memset(&saddr_nl, 0, sizeof(struct sockaddr_nl)); + saddr_nl.nl_family = AF_NETLINK; + saddr_nl.nl_pid = getpid(); + saddr_nl.nl_groups = 10; + + res = bind(s, (struct sockaddr *)&saddr_nl, sizeof(saddr_nl)); + if (res == -1) { + perror("nl_bind_socket"); + return -1; + } + return 1; +} + +int nl_ifw_create_socket(void) { + int s; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_IFWLOG); + if (s < 0) { + perror("nl_create_socket"); + return -1; + } + + if (nl_ifw_bind_socket(s) < 0) { + close(s); + fprintf(stderr, "bind failed\n"); + return -1; + } + + return s; +} + +int nl_ifw_read_msg(int s, struct nlmsghdr *nlh, struct nl_msg *msg) { + char buf[sizeof(struct nlmsghdr) + sizeof(struct nl_msg)]; + int ret; + + ret = recv(s, &buf, sizeof(buf), 0); + if (ret > 0) { + if (nlh) memcpy(nlh, buf, sizeof(struct nlmsghdr)); + if (msg) memcpy(msg, NLMSG_DATA(buf), sizeof(struct nl_msg)); + } + + return ret; +} diff --git a/src/plugins/ifw/libnl_ifw.h b/src/plugins/ifw/libnl_ifw.h new file mode 100644 index 0000000..896d05c --- /dev/null +++ b/src/plugins/ifw/libnl_ifw.h @@ -0,0 +1,35 @@ +/* + * libnl_ifw.h + */ + +#ifndef _LIBNL_IFW_H +#define _LIBNL_IFW_H + +#include +#include +#include +#include +#include +#include + +#define NETLINK_IFWLOG 19 + +#define PREFSIZ 32 + +struct nl_msg { /* Netlink kernel to user message */ + long timestamp_sec; /* time packet */ + char indev_name[IFNAMSIZ]; /* name of the ingoing interface */ + char outdev_name[IFNAMSIZ]; /* name of the outgoing interface */ + unsigned char prefix[PREFSIZ]; /* logging informations */ + struct iphdr ip; + union { + struct tcphdr th; + struct udphdr uh; + } h; +}; + +int nl_ifw_bind_socket(int s); +int nl_ifw_create_socket(void); +int nl_ifw_read_msg(int s, struct nlmsghdr *nlh, struct nl_msg *msg); + +#endif /* !_LIBNL_IFW_H */ diff --git a/src/plugins/ifw/list.h b/src/plugins/ifw/list.h new file mode 100644 index 0000000..1b55f95 --- /dev/null +++ b/src/plugins/ifw/list.h @@ -0,0 +1,155 @@ +#ifndef LIST_H +#define LIST_H + +/* borrowed from kernel header linux/list.h */ + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + + +/* borrowed from kernel header linux/kernel.h */ + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - linux_offsetof(type,member) );}) + + +/* borrowed from linux/stdddef.h */ + +#define linux_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#endif /* LIST_H */ diff --git a/src/plugins/ifw/plugin.c b/src/plugins/ifw/plugin.c new file mode 100644 index 0000000..0e6ac69 --- /dev/null +++ b/src/plugins/ifw/plugin.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ifw.h" +#include "ifw_dbus.h" +#include "ipset.h" + +static int init(plugin_t *plugin, DBusConnection *connection); +static void deinit(plugin_t *plugin, DBusConnection *connection); +static void process_attack(plugin_t *plugin, DBusConnection *connection, int seq, msg_usr_t *attack); +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); +#ifdef IFW_FAKE +static int generate_fake_attack(msg_usr_t *attack); +static void handle_fake(plugin_t *plugin, DBusConnection *connection); +#else +static void handle_incoming(plugin_t *plugin, DBusConnection *connection); +#endif + +static int init(plugin_t *plugin, DBusConnection *connection) { + ifw_t *ifw; + + ifw = malloc(sizeof(ifw_t)); + if (!ifw) { + fprintf(stderr, "unable to malloc ifw\n"); + return -1; + } + + report_list_init(&ifw->reports); + black_list_init(&ifw->blacklist); + white_list_init(&ifw->whitelist); + white_list_load(&ifw->whitelist, IFW_WHITELIST_FILENAME); + + ifw->mode = IFW_MODE_INTERACTIVE; + +#ifdef IFW_FAKE + plugin->fd = 0; +#else + plugin->fd = nl_ifw_create_socket(); + if (plugin->fd < 0) { + fprintf(stderr, "unable to init netlink\n"); + return -1; + } +#endif + + ifw_dbus_notify_clear(connection); + ifw_dbus_notify_init(connection); + + plugin->priv = (void *) ifw; + + return 0; +} + +static void deinit(plugin_t *plugin, DBusConnection *connection) { + ifw_t *ifw = (ifw_t *) plugin->priv; + + ifw_dbus_notify_clear(connection); + close(plugin->fd); +} + +#ifdef IFW_FAKE +#include + +static int generate_fake_attack(msg_usr_t *attack) { + static int seq = 0; + uint32_t addr = 0xC0A86401; + + time(&attack->timestamp_sec); + strcpy(attack->indev_name, "ppp0"); + attack->sensor = 0; + attack->protocol = 0; + attack->icmp_type = 0; + attack->d_port = 0; + attack->s_addr = htonl(addr); + + switch(seq%3) { + case 0: + strcpy(attack->prefix, "SCAN"); + break; + case 1: + strcpy(attack->prefix, "SERV"); + attack->d_port = 22; + break; + case 2: + strcpy(attack->prefix, "PASS"); + break; + } + + addr++; + seq++; + + return seq; +} + +static void handle_fake(plugin_t *plugin, DBusConnection *connection) { + msg_usr_t fake_attack; + int seq; + + read(0, NULL, 1); + + seq = generate_fake_attack(&fake_attack); + printf("seq : %d\n", seq); + + process_attack(plugin, connection, seq, &fake_attack); +} + +#else + +static void handle_incoming(plugin_t *plugin, DBusConnection *connection) { + struct nl_msg msg; + static int seq = 0; + msg_usr_t attack; + + if (nl_ifw_read_msg(plugin->fd, NULL, &msg) <= 0) { + fprintf(stderr, "unable to read packet from netlink\n"); + return; + } + + attack.timestamp_sec = msg.timestamp_sec; + strncpy(attack.indev_name, msg.indev_name, IFNAMSIZ); + strncpy(attack.prefix, (char *) msg.prefix, PREFSIZ); + attack.sensor = 0; + attack.protocol = msg.ip.protocol; + attack.s_addr = msg.ip.saddr; + switch (msg.ip.protocol) { + case IPPROTO_TCP: + attack.d_port = msg.h.th.dest; + break; + case IPPROTO_UDP: + attack.d_port = msg.h.uh.dest; + break; + default: + attack.d_port = 0; + break; + } + attack.icmp_type = 0; + + process_attack(plugin, connection, seq++, &attack); +} + +#endif + +static void process_attack(plugin_t *plugin, DBusConnection *connection, int seq, msg_usr_t *attack) { + ifw_t *ifw = (ifw_t *) plugin->priv; + report_list_cell_t *report; + + if (black_list_find(&ifw->blacklist, attack->s_addr) || + white_list_find(&ifw->whitelist, attack->s_addr) || + report_list_find(&ifw->reports, attack->s_addr, 0)) { + struct in_addr addr; + addr.s_addr = attack->s_addr; + fprintf(stderr, "skipping known address: %s\n", inet_ntoa(addr)); + return; + } + + if (!strcmp(attack->indev_name, "lo")) { + fprintf(stderr, "skipping loopback interface\n"); + return; + } + + report = report_list_add(&ifw->reports, seq, attack); + if (report) { + if (ifw->mode == IFW_MODE_AUTO) { + /* add ip address in ipset blacklist */ + ifw_dbus_apply_report_verdict(connection, ifw, report, 1); + } + + /* notify the attack to frontends */ + ifw_dbus_notify_attack(connection, report); + } + + black_list_print(&ifw->blacklist); + white_list_print(&ifw->whitelist); + report_list_print(&ifw->reports); +} + +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) { + ifw_t *ifw = (ifw_t *) plugin->priv; + if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetMode")) { + return ifw_dbus_get_mode(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SetMode")) { + return ifw_dbus_set_mode(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetReports")) { + return ifw_dbus_get_reports(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetBlacklist")) { + return ifw_dbus_get_blacklist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SetBlacklistVerdict")) { + return ifw_dbus_set_blacklist_verdict(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "UnBlacklist")) { + return ifw_dbus_unblacklist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "GetWhitelist")) { + return ifw_dbus_get_whitelist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "Whitelist")) { + return ifw_dbus_whitelist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "UnWhitelist")) { + return ifw_dbus_unwhitelist(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "ClearProcessedReports")) { + return ifw_dbus_clear_processed_reports(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SendAlertAck")) { + return ifw_dbus_send_alert_ack(connection, message, ifw); + } else if (dbus_message_is_method_call(message, IFW_DBUS_INTERFACE, "SendManageRequest")) { + return ifw_dbus_send_manage_request(connection, message, ifw); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +plugin_t ifw_plugin = { + .name = "Interactive Firewall", + .path = IFW_DBUS_PATH, + .init = init, +#ifdef IFW_FAKE + .handle_incoming = handle_fake, +#else + .handle_incoming = handle_incoming, +#endif + .handle_message = handle_message, + .deinit = deinit, +}; diff --git a/src/plugins/ifw/report_list.c b/src/plugins/ifw/report_list.c new file mode 100644 index 0000000..c21df7e --- /dev/null +++ b/src/plugins/ifw/report_list.c @@ -0,0 +1,81 @@ +#include "report_list.h" + +#include +#include + +void report_list_init(report_list_t *list) { + INIT_LIST_HEAD(list); +} + +report_list_cell_t *report_list_add(report_list_t *list, int seq, msg_usr_t *attack) { + report_list_cell_t *cell; + + cell = malloc(sizeof(report_list_cell_t)); + if (!cell) { + fprintf(stderr, "unable to alloc enough memory for report list cell, skipping\n"); + return NULL; + } + cell->seq = seq; + cell->info = *attack; + cell->processed = 0; + INIT_LIST_HEAD(&cell->list); + list_add_tail(&cell->list, list); + + return cell; +} + +report_list_cell_t *report_list_find(report_list_t *list, u_int32_t addr, int include_processed) { + struct list_head *entry; + + __list_for_each(entry, list) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + if (cell->info.s_addr == addr && include_processed || !cell->processed) { + return cell; + } + } + + return NULL; +} + +report_list_cell_t *report_list_find_seq(report_list_t *list, int seq) { + struct list_head *entry; + + __list_for_each(entry, list) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + if (cell->seq == seq) { + return cell; + } + } + + return NULL; +} + +void report_list_remove(report_list_cell_t *cell) { + list_del(&cell->list); + free(cell); +} + +void report_list_clear_processed(report_list_t *list) { + report_list_cell_t *cell, *n; + + list_for_each_entry_safe(cell, n, list, list) { + if (cell->processed) { + report_list_remove(cell); + } + } +} + +void report_list_print(report_list_t *list) { + struct list_head *entry; + + printf("* report list {\n"); + __list_for_each(entry, list) { + report_list_cell_t *cell; + cell = list_entry(entry, report_list_cell_t, list); + printf("%d,\n", cell->seq); + } + printf("} report list *\n"); +} + diff --git a/src/plugins/ifw/report_list.h b/src/plugins/ifw/report_list.h new file mode 100644 index 0000000..626afed --- /dev/null +++ b/src/plugins/ifw/report_list.h @@ -0,0 +1,24 @@ +#ifndef REPORT_LIST_H +#define REPORT_LIST_H + +#include "list.h" +typedef struct list_head report_list_t; + +#include "ifw.h" + +typedef struct { + struct list_head list; + int seq; + msg_usr_t info; + char processed; +} report_list_cell_t; + +void report_list_init(report_list_t *list); +report_list_cell_t *report_list_add(report_list_t *list, int seq, msg_usr_t *attack); +report_list_cell_t *report_list_find(report_list_t *list, u_int32_t addr, int include_processed); +report_list_cell_t *report_list_find_seq(report_list_t *list, int seq); +void report_list_remove(report_list_cell_t *cell); +void report_list_clear_processed(report_list_t *list); +void report_list_print(report_list_t *list); + +#endif /* REPORT_LIST_H */ diff --git a/src/plugins/ifw/white_list.c b/src/plugins/ifw/white_list.c new file mode 100644 index 0000000..4318abc --- /dev/null +++ b/src/plugins/ifw/white_list.c @@ -0,0 +1,119 @@ +#include "white_list.h" +#include "ipset.h" +#include "ifw.h" + +#include +#include +#include +#include +#include + +void white_list_init(white_list_t *list) { + INIT_LIST_HEAD(list); +} + +void white_list_add(white_list_t *list, u_int32_t addr) { + white_list_cell_t *cell; + + cell = malloc(sizeof(white_list_cell_t)); + if (!cell) { + fprintf(stderr, "unable to alloc enough memory for white list cell, skipping\n"); + return; + } + cell->addr = addr; + INIT_LIST_HEAD(&cell->list); + list_add_tail(&cell->list, list); + + ipset_whitelist_add(cell->addr); + white_list_save(list, IFW_WHITELIST_FILENAME); +} + +white_list_cell_t *white_list_find(white_list_t *list, u_int32_t addr) { + struct list_head *entry; + + __list_for_each(entry, list) { + white_list_cell_t *cell; + cell = list_entry(entry, white_list_cell_t, list); + if (cell->addr == addr) { + return cell; + } + } + + return NULL; +} + +void white_list_remove(white_list_t *list, u_int32_t addr) { + white_list_cell_t *cell, *n, *prev; + + ipset_whitelist_remove(addr); + + prev = NULL; + list_for_each_entry_safe(cell, n, list, list) { + if (prev) + free(prev); + if (cell->addr == addr) { + list_del(&cell->list); + prev = cell; + } else { + prev = NULL; + } + } + if (prev) + free(prev); + + white_list_save(list, IFW_WHITELIST_FILENAME); +} + + +void white_list_print(white_list_t *list) { + struct list_head *entry; + + printf("* white list {\n"); + __list_for_each(entry, list) { + white_list_cell_t *cell; + struct in_addr addr; + cell = list_entry(entry, white_list_cell_t, list); + addr.s_addr = cell->addr; + printf("%s,\n", inet_ntoa(addr)); + } + printf("} white list *\n"); +} + +void white_list_load(white_list_t *list, const char *filepath) { + FILE *fp; + + fp = fopen(filepath, "r"); + if (fp) { + char addr_str[16]; + struct in_addr addr; + while (fscanf(fp, "%15s\n", addr_str) > 0) { + if (inet_aton(addr_str, &addr)) { + white_list_add(list, addr.s_addr); + printf("adding IP address in white list: %s\n", addr_str); + } else { + fprintf(stderr, "unable to parse IP address in white list: %s\n", addr_str); + } + } + } else { + fprintf(stderr, "unable to open white list file\n"); + } +} + +void white_list_save(white_list_t *list, const char *filepath) { + FILE *fp; + struct list_head *entry; + + fp = fopen(filepath, "w+"); + if (fp) { + __list_for_each(entry, list) { + white_list_cell_t *cell; + struct in_addr addr; + cell = list_entry(entry, white_list_cell_t, list); + addr.s_addr = cell->addr; + fprintf(fp, "%15s\n", inet_ntoa(addr)); + printf("adding IP address in white list: %s\n", inet_ntoa(addr)); + } + } else { + fprintf(stderr, "unable to write white list file\n"); + } +} diff --git a/src/plugins/ifw/white_list.h b/src/plugins/ifw/white_list.h new file mode 100644 index 0000000..564f5ee --- /dev/null +++ b/src/plugins/ifw/white_list.h @@ -0,0 +1,23 @@ +#ifndef WHITE_LIST_H +#define WHITE_LIST_H + +#include "list.h" + +#include + +typedef struct list_head white_list_t; + +typedef struct { + struct list_head list; + u_int32_t addr; +} white_list_cell_t; + +void white_list_init(white_list_t *list); +void white_list_add(white_list_t *list, u_int32_t addr); +white_list_cell_t *white_list_find(white_list_t *list, u_int32_t addr); +void white_list_remove(white_list_t *list, u_int32_t addr); +void white_list_print(white_list_t *list); +void white_list_load(white_list_t *list, const char *filepath); +void white_list_save(white_list_t *list, const char *filepath); + +#endif /* WHITE_LIST_H */ diff --git a/src/plugins/wireless/plugin.c b/src/plugins/wireless/plugin.c new file mode 100644 index 0000000..1ddeb95 --- /dev/null +++ b/src/plugins/wireless/plugin.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include + +#include "wpa_ctrl.h" +#include "plugin.h" + +#define WIRELESS_PATH PLUGIN_ROOT_PATH "/wireless" +#define WIRELESS_INTERFACE PLUGIN_ROOT_INTF ".wireless" + +static int init(plugin_t *plugin, DBusConnection *connection); +static void deinit(plugin_t *plugin, DBusConnection *connection); +static void handle_incoming(plugin_t *plugin, DBusConnection *connection); +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); +static DBusHandlerResult select_network(DBusConnection *connection, DBusMessage *message, plugin_t *plugin); +static DBusHandlerResult wpa_supplicant_request(DBusConnection *connection, DBusMessage *message, plugin_t *plugin, char *cmd); + +static int init(plugin_t *plugin, DBusConnection *connection) { + struct wpa_ctrl *ctrl_conn = NULL; + const char *ctrl_iface_dir = "/var/run/wpa_supplicant"; + DIR *dir = opendir(ctrl_iface_dir); + + if (dir) { + struct dirent *dent; + while ((dent = readdir(dir))) { + char *cfile; + int flen; + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + flen = strlen(ctrl_iface_dir) + strlen(dent->d_name) + 2; + cfile = malloc(flen); + if (cfile == NULL) + return -1; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, dent->d_name); + ctrl_conn = wpa_ctrl_open(cfile); + free(cfile); + break; + } + closedir(dir); + } + + if (ctrl_conn != NULL) { + plugin->fd = wpa_ctrl_get_fd(ctrl_conn); + } else { + /* do not fail, the plugin will try to re-init when needed */ + plugin->fd = -1; + } + plugin->priv = (void *) ctrl_conn; + return 0; +} + +static void deinit(plugin_t *plugin, DBusConnection *connection) { + if (plugin->fd > 0) + close(plugin->fd); +} + +static void handle_incoming(plugin_t *plugin, DBusConnection *connection) { + struct wpa_ctrl *ctrl_conn = (struct wpa_ctrl *) plugin->priv; + char buf[2048]; + size_t len; + wpa_ctrl_recv(ctrl_conn, buf, &len); + buf[len] = '\0'; + printf("received event: %s\n", buf); +} + +static DBusHandlerResult handle_message(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) { + if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "ScanResults")) { + return wpa_supplicant_request(connection, message, plugin, "SCAN_RESULTS"); + } else if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "ListNetworks")) { + return wpa_supplicant_request(connection, message, plugin, "LIST_NETWORKS"); + } else if (dbus_message_is_method_call(message, WIRELESS_INTERFACE, "SelectNetwork")) { + return select_network(connection, message, plugin); + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult select_network(DBusConnection *connection, DBusMessage *message, plugin_t *plugin) { + DBusError error; + u_int32_t net; + char cmd[32]; + + dbus_error_init (&error); + if (!dbus_message_get_args (message, + &error, + DBUS_TYPE_UINT32, + &net, + DBUS_TYPE_INVALID)) { + fprintf(stderr, "select_network(): failed to read D-BUS message args: %s\n", error.message); + dbus_error_free (&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + dbus_error_free (&error); + + snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %u", net); + return wpa_supplicant_request(connection, message, plugin, cmd); +} + +static DBusHandlerResult wpa_supplicant_request(DBusConnection *connection, DBusMessage *message, plugin_t *plugin, char *cmd) { + struct wpa_ctrl *ctrl_conn = (struct wpa_ctrl *) plugin->priv; + DBusMessage *reply; + char buf[2048]; + size_t len; + int ret = -1; + + len = sizeof(buf) - 1; + if (ctrl_conn) { + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, &len, NULL); + } + if (ret == -1) { + fprintf(stderr, "connection to wpa_supplicant daemon lost, reconnecting\n"); + init(plugin, connection); + ctrl_conn = (struct wpa_ctrl *) plugin->priv; + if (ctrl_conn) { + ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, &len, NULL); + } + } + if (ret != 0) { + fprintf(stderr, "unable to request command\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + buf[len] = '\0'; + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, + DBUS_TYPE_STRING, + buf, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); + dbus_connection_flush(connection); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +plugin_t wpa_supplicant_plugin = { + .name = "Wireless", + .path = WIRELESS_PATH, + .init = init, + .handle_incoming = handle_incoming, + .handle_message = handle_message, + .deinit = deinit, +}; diff --git a/src/plugins/wireless/wpa_ctrl.c b/src/plugins/wireless/wpa_ctrl.c new file mode 100644 index 0000000..1c88e69 --- /dev/null +++ b/src/plugins/wireless/wpa_ctrl.c @@ -0,0 +1,238 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen + * + * 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. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "wpa_ctrl.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + + +/** + * struct wpa_ctrl - Internal structure for control interface library + * + * This structure is used by the wpa_supplicant/hostapd control interface + * library to store internal data. Programs using the library should not touch + * this data directly. They can only use the pointer to the data structure as + * an identifier for the control interface connection and use this as one of + * the arguments for most of the control interface library functions. + */ +struct wpa_ctrl { + int s; +#ifdef CONFIG_CTRL_IFACE_UDP + struct sockaddr_in local; + struct sockaddr_in dest; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UDP */ +}; + + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; +#ifndef CONFIG_CTRL_IFACE_UDP + static int counter = 0; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + ctrl = malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + perror("socket"); + free(ctrl); + return NULL; + } + + ctrl->local.sin_family = AF_INET; + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(9877); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + perror("connect"); + close(ctrl->s); + free(ctrl); + return NULL; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sun_family = AF_UNIX; + snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", + ctrl_path); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + free(ctrl); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + unlink(ctrl->local.sun_path); +#endif /* CONFIG_CTRL_IFACE_UDP */ + close(ctrl->s); + free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + int res; + fd_set rfds; + + if (send(ctrl->s, cmd, cmd_len, 0) < 0) + return -1; + + for (;;) { + tv.tv_sec = 2; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(ctrl->s, &rfds)) { + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + if (res > 0 && reply[0] == '<') { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if ((size_t) res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + int res; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} diff --git a/src/plugins/wireless/wpa_ctrl.h b/src/plugins/wireless/wpa_ctrl.h new file mode 100644 index 0000000..964f7ab --- /dev/null +++ b/src/plugins/wireless/wpa_ctrl.h @@ -0,0 +1,179 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen + * + * 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. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** wpa_supplicant is exiting */ +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +/** Password change was completed successfully */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " + + +/* wpa_supplicant/hostapd control interface access */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ +void wpa_ctrl_close(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to two seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. This + * function will block if no messages are available. The received response will + * be written to reply and reply_len is set to the actual length of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: Non-zero if there are pending messages + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ -- cgit v1.2.1