I'm developing a Linux network server application, and would like to
have the ability to not accept a TCP/IP connection if it does not fall
within a list of valid addresses. I would like to do this
programmaticly, and need to be able to do it within my own program, in
user-space (kernel level mechanisms, such as IPChains/IPTables will
not work, as the list of IP Addresses that need to be denied changes
rapidly and dynamically based on who has connected. My program also
needs to be used by users who do not have root access).
In psudo-code, I would prefer my logic to look as follows:
myfd = socket(AF_INET, SOCK_STREAM, 0)
bind(myfd, ServerPort)
listen(myfd, 100)
// Wait for an incoming connection
select(myfd)
// Get the Remote IP Address of the pending connection
getpeername(myfd, &remoteAddress)
// Call my routine to determine if the connection
// source IP Address is allowed
if(isAllowed(remoteAddress))
{
accept(myfd, NULL, NULL)
// Do items
}
else
{
// Remove/close the connection from the listener without accepting
it
reject(myfd)
}
Specifically, at the TCP/IP packet level, I would like to be able to
decide to send a RST (or nothing at all) in response to the SYN
establishing the connection.
Can anyone make a recommendation on how to accomplish this using the
standard Linux networking apis?
(For more information on other people's attempts to do the same thing,
back in 1995, see <http://www.uwsg.iu.edu/hypermail/linux/net/9508.2/0063.html>
.) |
Request for Question Clarification by
ufphoenix-ga
on
04 Nov 2002 14:26 PST
What about if you accept() the connection and then do a getpeername()
on it? Otherwise, you would have to go to a much lower level than the
standard socket() calls.
|
Clarification of Question by
dslik-ga
on
04 Nov 2002 17:39 PST
My current code calls accept, then uses getpeername to retreive the
remote IP address. (similarly as what is showen in the psudocode) If
it does not match my valid list, I immediatly close the connection.
However, this still results in the connection being opened, then
closed, and allows the service to show up on port mapping tools
running on hosts that will be denied if they try to connect.
I've done some work using libpcap to send the rst packets, but that
requires root access, and makes the rest of the TCP/IP connection
handling much more difficult.
I could do what I'm looking for using a loadable Kernel Module that
manipulates the TCP/IP structures directly and exposes the required
functionality to user-space, but once again, that requires root.
(Although this is by far the cleanest way to do it...)
|
Clarification of Question by
dslik-ga
on
06 Nov 2002 21:41 PST
Here's another reference to someone else trying to accomplish the same
thing.
<http://www.faqchest.com/prgm/apach-l/apach-98/apach-9806/apach-980612/apach98062318_29218.html>
I looked into the user space BNF filtering suggestion at:
<http://snafu.freedom.org/linux2.2/docs/filter.txt>
and
<http://www.fedchik.org.ua/linux/netfilter/Sniffing-bytes.html>
However, this had several disadvantages:
1) Measureable impacts on network throughput, especially with BNF
programs built from deny lists with over 100 IP Address entries. (I
used a modified output based on tcpdump -d, without any optomizations)
2) Each time the deny list changes, the BNF has to be re-written and
re-attatched to the socket. It would also require some development to
write a program to create the BNF compiled program from the IP Address
deny list. This is a non-trivial undertaking.
3) I never see the attempts to connect, so I can't log them.
So, close, but not quite...
Note: This is quite a powerful capability, and will most likely be
useful for me in other applications.
|
Request for Question Clarification by
maniac-ga
on
07 Nov 2002 17:39 PST
Hello Dslik,
A very interesting concept.
I can see at least two ways to do this - both written up in the
November 2002 Linux Journal. Let me provide a summary of each and see
if you want an answer that expands on one or the other or both.
[1] Use ptrace. Ptrace provides a way for a parent process to
intercept the system calls of a child process and make modifications.
In your case, trap the accept call, check the IP address in the
parent, and pass the data through (if OK) or make it invalid (if not
OK).
[2] Use the Linux Security Module, recently introduced to the 2.5
kernel series. In this case, use a loadable module to intercept the
accept and have a device interface (perhaps /proc) to have the user
application load the settings for the accept / reject criteria.
In a brief look, the first looks more suitable for your application
since it does not require a kernel module, but I can't tell for sure.
Please provide guidance so I (or another) can answer your question.
--Maniac
|
Clarification of Question by
dslik-ga
on
07 Nov 2002 21:27 PST
(Wanders over and grabs the unread copy of Linux Journal ...)
[1] - The problem is that I haven't been able to find a way to get the
IP Address of the incoming connection from user space before accept is
called, and I don't have a way to close that pending connecting before
calling accept.
Your suggested approach would work great for making a simple program
(eg, blacklist apache), that, behind apache's (or any other programs)
back prevents any blacklisted IP address from being able to connect.
Furthermore, as you have mentioned, it would not even require any
modifications to the program.
While this is VERY cool, it dosn't help with my problem of wanting the
connecting side not see the completion of the TCP/IP three way
handshake if they are on the blacklist. With this approach, while the
application would never see the connection, the connecting side would
see a "connected/disconnected" sequence.
(Side note: What a cool way to write an anti-spam protection program
for sendmail, etc! On each incoming TCP/IP connection, check against a
downloaded list of spammer's IP addresses before allowing it to
connect - or - even better, open a TCP/IP connection back out to the
originating IP Address, and check to make sure that it's a valid mail
server, and not an open relay)
[2] - My understanding is that this would require loading a kernel
module, which requires root access.
I've been looking at writing a loadable kernel module already as a
fallback position, and it looks like it would be do-able without
having to use the new 2.5 features. (I'm still coming up to speed on
the 2.4 changes to the networking code to figure out how to
retreive/punt the pending connection off the accept queue).
However, this approach still needs root, so I'm considering it as a
fallback position. Perhaps, if no other mechanisms turn up, I'll have
some more specific questions related to writing the loadable kernel
module...
(The USB security example is neat. This is a simple and easy way to
protect your system. This alone is worth upgrading to 2.5)
|
Request for Question Clarification by
maniac-ga
on
08 Nov 2002 17:38 PST
Hello Dslik,
I did find another possibility from man pages on a Unix system that
*may* be supported on Linux, I can't tell for sure (and why it is not
an answer). Here is the relevant text from man 2 accept.
>One can obtain user connection request data without confirming the
con-
>nection by issuing a recvmsg(2) call with an msg_iovlen of 0 and a
non-
>zero msg_controllen, or by issuing a getsockopt(2) request.
Similarly,
>one can provide user connection rejection information by issuing a
>sendmsg(2) call with providing only the control information, or by
call-
>ing setsockopt(2).
I assume from this you could get the address from the first two
parameters in the msghdr structure passed in to the recvmsg call.
Here are a few pointers (but not specific to TCP/IP on Linux) that are
relevant.
NetBSD (Mac OS X) man pages for accept, recvmsg, etc.
http://www.hmug.org/man/2/accept.html
http://www.hmug.org/man/2/recvmsg.html
Linux DECnet programming
http://sucs.swan.ac.uk/~rohan/DECnet/prog.html
Near the bottom it describes "System Calls" and indicates you set
"half connection mode" using setsockopt or use the recvmsg method.
There are several other sources that indicate that you can only reject
the connection after accepting it. Some search phrases that could be
used include
reject TCP/IP connection
recvmsg reject connection example
Related to my original #2 (kernel module), you would build the kernel
module (and install as root), but user would use a non-privileged
interface - /proc is convenient - to set the addresses to accept or
reject.
In either case, let me know if you want me to pursue one of these in
more detail.
--Maniac
|
Clarification of Question by
dslik-ga
on
09 Nov 2002 19:43 PST
Looks promising... Although I'm not having very much success using the
described approach on Linux (2.2/2.4) (or on Solaris). I'll try it on
a BSD system on Tuesday when I have access to an OSX box again.
Also, from the Linux 2.4 source code (socket.c)
> (Posix) 1003.1g adds the ability to recvmsg() to query
> connection pending status to recvmsg. We need to add that
> support in a way thats clean when we restucture accept also.
This seems to insinuate that this is not yet supported in Linux.
(also see <http://www.iglu.org.il/lxr/source/net/decnet/TODO> )
I'll provide more details on Tuesday evening after I've tried this on
OSX where it's supposed to work.
|
Clarification of Question by
dslik-ga
on
13 Nov 2002 18:08 PST
I got my test program using recvmsg running on Darwin, and it always
reports errno 57, Socket is not connected. This is after select has
returned true on the file descriptor that the connection is ready to
read, and accept works, if called. On linux, it returns errno 22,
Invalid argument.
Based on the below quote from the recvmsg man page, I am guessing that
this function will not work in the way that I would like it to if the
socket is a TCP/IP socket.
>If from is non-null and the socket is not connection-oriented,
>the source address of the message is filled in.
|