Commit f10e52ae authored by Patrick J Cherry's avatar Patrick J Cherry
Browse files

Added new utmp parser

parent eab4fbfd
......@@ -30,11 +30,16 @@ manpage: ./bin/firewall man/firewall-whitelist.man
all: manpage
test:
distclean: clean
test: ext/symbiosis_utmp.so
cd test && ruby ./tc_symbiosis_utmp.rb
if [ ! -d ./i ]; then mkdir ./i ; fi
if [ ! -d ./i/incoming.d/ ]; then mkdir ./i/incoming.d/; fi
if [ ! -d ./i/outgoing.d/ ]; then mkdir ./i/outgoing.d/; fi
if [ ! -d ./i/blacklist.d/ ]; then mkdir ./i/blacklist.d/; fi
touch ./i/incoming.d/00-ssh
touch ./i/incoming.d/05-ping
sudo ./bin/firewall --incoming-d=./i/incoming.d --outgoing-d=./i/outgoing.d/ --rule-d=./rule.d/ --no-exec --verbose --blacklist-d=./i/blacklist.d --whitelist-d=./i/whitelist.d --no-delete --verbose
fakeroot ./bin/firewall --incoming-d=./i/incoming.d --outgoing-d=./i/outgoing.d/ --rule-d=./rule.d/ --no-exec --verbose --blacklist-d=./i/blacklist.d --whitelist-d=./i/whitelist.d --no-delete --verbose
.PHONY: test
#!/usr/bin/ruby
#! /usr/bin/ruby1.8
#
# NAME
#
......@@ -32,6 +32,7 @@
require 'getoptlong'
require 'ipaddr'
require 'symbiosis/utmp'
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
......@@ -97,18 +98,10 @@ end
puts "Expiring done - removed #{expired} file(s)" if ( $VERBOSE )
ip_addresses = {}
#
# Run last to find the IPs, store unique ones in a hash.
# Fetch the IP addresses
#
IO.popen("/usr/bin/last -i") {|pipe| pipe.readlines}.each do |l|
next unless l =~ /[ \t]((\d{1,3}\.){3,3}\d{1,3}|[0-9a-f:]+)[ \t]/
ip_addresses[ $1 ] = 1
end
ip_addresses = Symbiosis::Utmp.read.collect{|entry| entry["ip"]}
#
# Did we update?
......@@ -118,9 +111,21 @@ updated=false
#
# Iterate over each IP
#
ip_addresses.each_key do |ip|
if ( ( ip =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/ ) &&
( ip !~ /^(127|0)\./ ) )
ip_addresses.each do |ip|
#
# We only want IP addresses.
#
next unless ip.is_a?(IPAddr)
#
# FIXME: Need better IPv6 conditions.
#
if ( ( ip.ipv4? and
!( IPAddr.new("127.0.0.1/8").include?(ip) or IPAddr.new("0.0.0.0") == ip )
) or (
ip.ipv6? and IPAddr.new("2000::/3").include?(ip)
)
)
puts "Found IP address: #{ip}" if ( $VERBOSE )
......
......@@ -3,12 +3,12 @@ Section: net
Priority: extra
Maintainer: Steve Kemp <steve@bytemark.co.uk>
Uploaders: Patrick J Cherry <patch@bytemark.co.uk>
Build-Depends: debhelper (>= 7.0.0), cdbs, txt2man
Build-Depends: debhelper (>= 7.0.0), cdbs, txt2man, ruby-pkg-tools, ruby-dev
Standards-Version: 3.8.0
Package: symbiosis-firewall
Architecture: all
Depends: iptables, dnsutils, iproute, ruby
Architecture: any
Depends: iptables, dnsutils, iproute, ruby, ${shlibs:Depends}, ${misc:Depends}
Replaces: bytemark-vhost-ssh-protection, bytemark-vhost-firewall
Conflicts: bytemark-vhost-firewall, bytemark-vhost-ssh-protection (<< 20081110153344)
Suggests: libnet-dns-perl
......
......@@ -3,6 +3,7 @@
#export DH_VERBOSE=1
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/ruby-pkg-tools/1/class/ruby-setup-rb.mk
build/symbiosis-firewall::
make all
......
require 'mkmf'
create_makefile('symbiosis_utmp')
#include <utmp.h>
#include <ruby.h>
#include <arpa/inet.h>
// Module and classes.
static VALUE mSymbiosis;
static VALUE cUtmp;
/*
* The logic to determine if an address is IPv4 or 6 is taken from last.c in
* sysvinit.
*
*/
VALUE
get_ip_addr (int32_t * src)
{
struct in_addr in4;
struct in6_addr in6;
unsigned int topnibble;
unsigned int azero = 0, sitelocal = 0;
int mapped = 0;
char ip_dst[INET_ADDRSTRLEN];
char ip6_dst[INET6_ADDRSTRLEN];
/*
* Return nil if there is no IP address
*/
if (src[0] + src[1] + src[2] + src[3] == 0)
return Qnil;
/*
* IPv4 or IPv6 ? We use 2 heuristics:
* 2. Current IPv6 range uses 2000-3fff or fec0-feff.
* Outside of that is illegal and must be IPv4.
* 2. If last 3 bytes are 0, must be IPv4
* 3. If IPv6 in IPv4, handle as IPv4
*
* Ugly.
*/
if (src[0] == 0 && src[1] == 0 && src[2] == htonl (0xffff))
mapped = 1;
topnibble = ntohl ((unsigned int) src[0]) >> 28;
azero = ntohl ((unsigned int) src[0]) >> 16;
sitelocal = (azero >= 0xfec0 && azero <= 0xfeff) ? 1 : 0;
if (((topnibble < 2 || topnibble > 3) && (!sitelocal)) || mapped ||
(src[1] == 0 && src[2] == 0 && src[3] == 0))
{
/* IPv4 */
in4.s_addr = mapped ? src[3] : src[0];
inet_ntop (AF_INET, &in4, ip_dst, INET_ADDRSTRLEN);
return rb_str_new2 (ip_dst);
}
else
{
/* IPv6 */
memcpy (in6.s6_addr, src, 16);
inet_ntop (AF_INET6, &in6, ip6_dst, INET6_ADDRSTRLEN);
return rb_str_new2 (ip6_dst);
}
}
/*
* Return a string or nil
*/
static VALUE
string_or_nil (char *str)
{
if (strlen (str) == 0)
{
return Qnil;
}
else
{
return rb_str_new2 (str);
}
}
/*
* Actually read the utmp file, returning an array of hash entries.
*/
static VALUE
cUtmp_read (int argc, VALUE * argv, VALUE self)
{
VALUE result = rb_ary_new ();
VALUE filename;
struct utmp *line;
/*
* Parse the args
*/
rb_scan_args (argc, argv, "01", &filename);
/*
* Set the filename and rewind the pointer
*/
if (TYPE (filename) == T_STRING)
{
utmpname (RSTRING (filename)->ptr);
}
else
{
utmpname (_PATH_WTMP);
}
setutent ();
while ((line = getutent ()) != NULL)
{
VALUE entry = rb_hash_new ();
/*
* This is to create IPAddr.new below
*/
VALUE ipaddr_args[1];
rb_hash_aset (entry,
rb_str_new2("user"),
string_or_nil (line->ut_user));
rb_hash_aset (entry,
rb_str_new2("pid"), INT2FIX (line->ut_pid));
// Set a ruby time.
rb_hash_aset (entry,
rb_str_new2("time"),
rb_time_new (line->ut_tv.tv_sec, line->ut_tv.tv_usec));
rb_hash_aset (entry,
rb_str_new2("type"), INT2FIX (line->ut_type));
rb_hash_aset (entry,
rb_str_new2("line"),
string_or_nil (line->ut_line));
rb_hash_aset (entry,
rb_str_new2("host"),
string_or_nil (line->ut_host));
ipaddr_args[0] = get_ip_addr (line->ut_addr_v6);
if (TYPE (ipaddr_args[0]) == T_STRING)
{
rb_hash_aset (entry,
rb_str_new2("ip"),
rb_class_new_instance (1,
ipaddr_args,
rb_const_get (rb_cObject,
rb_intern
("IPAddr"))));
}
else if (TYPE (ipaddr_args[0]) == T_NIL)
{
rb_hash_aset (entry, rb_str_new2("ip"), ipaddr_args[0]);
}
rb_ary_push (result, entry);
}
free (line);
return (result);
}
// Define module, classes and methods.
void
Init_symbiosis_utmp ()
{
mSymbiosis = rb_define_module ("Symbiosis");
cUtmp = rb_define_class_under (mSymbiosis, "Utmp", rb_cArray);
rb_require ("ipaddr");
rb_define_singleton_method (cUtmp, "read", cUtmp_read, -1);
}
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <unistd.h>
#include <utmp.h>
#include <arpa/inet.h>
int
main(int argc, char *argv[])
{
struct utmp entry;
int addr[4];
utmpname ("wtmp-test");
/* rewind file */
setutent();
/* First entry */
entry.ut_type = USER_PROCESS;
entry.ut_pid = 1001;
strncpy(entry.ut_line, "pts/10", UT_LINESIZE);
strncpy(entry.ut_id, "/10", sizeof(entry.ut_id));
/* Fri Jul 02 08:00:00 +0100 2010 */
entry.ut_tv.tv_sec = 1278054000;
entry.ut_tv.tv_usec = 135790;
strncpy(entry.ut_user, "alice", UT_NAMESIZE);
strncpy(entry.ut_host, "office.my-brilliant-site.com", UT_HOSTSIZE);
/*
* irb(main):003:0> IPAddr.new("1.2.3.4").to_i
* => 16909060
*
* gotta convert that from host to network byte order.
*
*/
entry.ut_addr_v6[0] = htonl(16909060);
entry.ut_addr_v6[1] = 0;
entry.ut_addr_v6[2] = 0;
entry.ut_addr_v6[3] = 0;
pututline(&entry);
/*
* Next entry -- IPv6
*
*/
entry.ut_type = USER_PROCESS;
entry.ut_pid = 2001;
strncpy(entry.ut_line, "pts/11", UT_LINESIZE);
strncpy(entry.ut_id, "/11", sizeof(entry.ut_id));
/* Fri Jul 02 08:30:00 +0100 2010 */
entry.ut_tv.tv_sec = 1278055800;
entry.ut_tv.tv_usec = 654321;
strncpy(entry.ut_user, "bob", UT_NAMESIZE);
strncpy(entry.ut_host, "shop.my-brilliant-site.com", UT_HOSTSIZE);
/*
* 2001:ba8:dead:beef:cafe::1
*/
entry.ut_addr_v6[0] = htonl(0x20010ba8);
entry.ut_addr_v6[1] = htonl(0xdeadbeef);
entry.ut_addr_v6[2] = htonl(0xcafe0000);
entry.ut_addr_v6[3] = htonl(0x00000001);
pututline(&entry);
/*
*
* Next -- mapped IPv6
*
*/
entry.ut_type = USER_PROCESS;
entry.ut_pid = 3001;
strncpy(entry.ut_line, "pts/12", UT_LINESIZE);
strncpy(entry.ut_id, "/12", sizeof(entry.ut_id));
/* Fri Jul 02 09:00:00 +0100 2010 */
entry.ut_tv.tv_sec = 1278057600;
entry.ut_tv.tv_usec = 24680;
strncpy(entry.ut_user, "charlie", UT_NAMESIZE);
strncpy(entry.ut_host, "garage.my-brilliant-site.com", UT_HOSTSIZE);
/*
* irb(main):002:0> IPAddr.new("::ffff:192.0.2.128")
* => #<IPAddr: IPv6:0000:0000:0000:0000:0000:ffff:c000:0280/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>
*
*/
entry.ut_addr_v6[0] = htonl(0);
entry.ut_addr_v6[1] = htonl(0);
entry.ut_addr_v6[2] = htonl(0x0000ffff);
entry.ut_addr_v6[3] = htonl(0xc0000280);
pututline(&entry);
endutent();
exit(EXIT_SUCCESS);
}
$: << "../lib/"
$: << "../ext/"
require 'symbiosis/utmp'
require 'test/unit'
require 'fileutils'
class TestUtmp < Test::Unit::TestCase
def setup
@wtmp = "wtmp-test"
system("gcc create-wtmp-test.c -o create-wtmp-test")
FileUtils.touch(@wtmp)
system("./create-wtmp-test")
end
def teardown
FileUtils.rm_f(@wtmp)
FileUtils.rm_f("create-wtmp-test")
end
def test_read
wtmp = nil
assert(File.exists?(@wtmp))
assert_nothing_raised {
wtmp = Symbiosis::Utmp.read(@wtmp);
}
assert_equal(3, wtmp.length)
[
[7, 1001, "pts/10", "alice", Time.at(1278054000.135790), "office.my-brilliant-site.com", IPAddr.new("1.2.3.4")],
[7, 2001, "pts/11", "bob", Time.at(1278055800.654321), "shop.my-brilliant-site.com", IPAddr.new("2001:ba8:dead:beef:cafe::1")],
[7, 3001, "pts/12", "charlie", Time.at(1278057600.024680), "garage.my-brilliant-site.com", IPAddr.new("192.0.2.128")]
].zip(wtmp).each do |b|
type, pid, line, user, time, host, ip, entry = b.flatten
assert_equal(type, entry["type"])
assert_equal(pid, entry["pid"])
assert_equal(line, entry["line"])
assert_equal(user, entry["user"])
assert_equal(time, entry["time"])
assert_equal(host, entry["host"])
assert_equal(ip, entry["ip"])
end
end
end
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment