File: p0f

package info (click to toggle)
qpsmtpd 0.84-9
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 1,376 kB
  • sloc: perl: 8,012; sh: 382; makefile: 61
file content (99 lines) | stat: -rw-r--r-- 2,724 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# -*- perl -*-

=pod

An Identification Plugin

 ./p0f -u qpsmtpd -d -q -Q /tmp/.p0f_socket 'dst port 25' -o /dev/null && \
    chown qpsmtpd /tmp/.p0f_socket

and add 

 ident/p0f /tmp/.p0f_socket 

to config/plugins

it puts things into the 'p0f' connection notes so other plugins can do
things based on source OS.

All code heavily based upon the p0fq.pl included with the p0f distribution.

=cut

use IO::Socket;
use Net::IP;

my $QUERY_MAGIC = 0x0defaced;

sub register {
  my ($self, $qp, $p0f_socket) = @_;

  $p0f_socket =~ /(.*)/; # untaint
  $self->{_args}->{p0f_socket} = $1;
}

sub hook_connect {
  my($self, $qp) = @_;

  my $p0f_socket = $self->{_args}->{p0f_socket};
  my $srcport    = 
  my $destport   = $self->qp->connection->local_port;

  my $src = new Net::IP ($self->qp->connection->remote_ip) 
    or $self->log(LOGERROR, "p0f: ".Net::IP::Error()), return (DECLINED);
  my $dst = new Net::IP ($self->qp->connection->local_ip) 
    or $self->log(LOGERROR, "p0f: ".NET::IP::Error()), return (DECLINED);
  my $query = pack("L L L N N S S",
                   $QUERY_MAGIC, 
                   1, 
                   rand ^ 42 ^ time,
                   $src->intip(), 
                   $dst->intip(), 
                   $self->qp->connection->remote_port,
                   $self->qp->connection->local_port);

  # Open the connection to p0f
  socket(SOCK, PF_UNIX, SOCK_STREAM, 0) 
    or $self->log(LOGERROR, "p0f: socket: $!"), return (DECLINED);
  connect(SOCK, sockaddr_un($p0f_socket)) 
    or $self->log(LOGERROR, "p0f: connect: $!"), return (DECLINED);
  defined syswrite SOCK, $query 
    or $self->log(LOGERROR, "p0f: write: $!"), close SOCK, return (DECLINED);

  my $response;
  defined sysread SOCK, $response, 1024 
    or $self->log(LOGERROR, "p0f: read: $!"), close SOCK, return (DECLINED);
  close SOCK;

  # Extract the response from p0f
  my ($magic, $id, $type, $genre, $detail, $dist, $link, $tos, $fw,
      $nat, $real, $score, $mflags, $uptime) =
        unpack ("L L C Z20 Z40 c Z30 Z30 C C C s S N", $response);

  if ($magic != $QUERY_MAGIC) {
	$self->log(LOGERROR, "p0f: Bad response magic.");
	return (DECLINED);
  }
  if ($type == 1) {
	$self->log(LOGERROR, "p0f: P0f did not honor our query");
	return (DECLINED);
  }
  if ($type == 2) {
	$self->log(LOGWARN, "p0f: This connection is no longer in the cache");
	return (DECLINED);
  }

  my $p0f = { 
      genre    => $genre,
      detail   => $detail,
      distance => $dist,
      link     => $link,
      uptime   => $uptime,
  };

  $self->qp->connection->notes('p0f', $p0f);
  $self->log(LOGINFO, "Results: ".$p0f->{genre}." (".$p0f->{detail}.")");
  $self->log(LOGERROR,"error: $@") if $@;

  return DECLINED;
}