/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
3 by Björn Påhlsson
Python based server
1
#!/usr/bin/python
2
3
import SocketServer
4
import socket
5
import select
6
from optparse import OptionParser
7
import datetime
8
import errno
9
import gnutls.crypto
10
import gnutls.connection
11
import gnutls.errors
12
import ConfigParser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
13
import sys
3 by Björn Påhlsson
Python based server
14
15
class Client(object):
16
    def __init__(self, name=None, dn=None, password=None,
17
                 passfile=None, fqdn=None, timeout=None,
18
                 interval=-1):
19
        self.name = name
20
        self.dn = dn
21
        if password:
22
            self.password = password
23
        elif passfile:
24
            self.password = open(passfile).readall()
25
        else:
26
            print "No Password or Passfile in client config file"
27
            # raise RuntimeError XXX
28
            self.password = "gazonk"
29
        self.fqdn = fqdn
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
30
        self.created = datetime.datetime.now()
3 by Björn Påhlsson
Python based server
31
        self.last_seen = None
32
        if timeout is None:
33
            timeout = self.server.options.timeout
34
        self.timeout = timeout
35
        if interval == -1:
36
            interval = self.server.options.interval
37
        self.interval = interval
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
38
        self.next_check = datetime.datetime.now()
3 by Björn Påhlsson
Python based server
39
40
def server_bind(self):
41
    if self.options.interface:
42
        if not hasattr(socket, "SO_BINDTODEVICE"):
43
            # From /usr/include/asm-i486/socket.h
44
            socket.SO_BINDTODEVICE = 25
45
        try:
46
            self.socket.setsockopt(socket.SOL_SOCKET,
47
                                   socket.SO_BINDTODEVICE,
48
                                   self.options.interface)
49
        except socket.error, error:
50
            if error[0] == errno.EPERM:
51
                print "Warning: Denied permission to bind to interface", \
52
                      self.options.interface
53
            else:
54
                raise error
55
    return super(type(self), self).server_bind()
56
57
58
def init_with_options(self, *args, **kwargs):
59
    if "options" in kwargs:
60
        self.options = kwargs["options"]
61
        del kwargs["options"]
62
    if "clients" in kwargs:
63
        self.clients = kwargs["clients"]
64
        del kwargs["clients"]
65
    if "credentials" in kwargs:
66
        self.credentials = kwargs["credentials"]
67
        del kwargs["credentials"]
68
    return super(type(self), self).__init__(*args, **kwargs)
69
70
71
class udp_handler(SocketServer.DatagramRequestHandler, object):
72
    def handle(self):
73
        self.wfile.write("Polo")
74
        print "UDP request answered"
75
76
77
class IPv6_UDPServer(SocketServer.UDPServer, object):
78
    __init__ = init_with_options
79
    address_family = socket.AF_INET6
80
    allow_reuse_address = True
81
    server_bind = server_bind
82
    def verify_request(self, request, client_address):
83
        print "UDP request came"
84
        return request[0] == "Marco"
85
86
87
class tcp_handler(SocketServer.BaseRequestHandler, object):
88
    def handle(self):
89
        print "TCP request came"
90
        print "Request:", self.request
91
        print "Client Address:", self.client_address
92
        print "Server:", self.server
93
        session = gnutls.connection.ServerSession(self.request,
94
                                                  self.server.credentials)
95
        session.handshake()
96
        if session.peer_certificate:
97
            print "DN:", session.peer_certificate.subject
98
        try:
99
            session.verify_peer()
100
        except gnutls.errors.CertificateError, error:
101
            print "Verify failed", error
102
            session.bye()
103
            return
104
        try:
105
            session.send(dict((client.dn, client.password)
106
                              for client in self.server.clients)
107
                         [session.peer_certificate.subject])
108
        except KeyError:
109
            session.send("gazonk")
110
            # Log maybe? XXX
111
        session.bye()
112
113
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
114
    __init__ = init_with_options
115
    address_family = socket.AF_INET6
116
    allow_reuse_address = True
117
    request_queue_size = 1024
118
    server_bind = server_bind
119
120
121
in6addr_any = "::"
122
123
cred = None
124
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
125
def string_to_delta(interval):
126
    """Parse a string and return a datetime.timedelta
127
128
    >>> string_to_delta('7d')
129
    datetime.timedelta(7)
130
    >>> string_to_delta('60s')
131
    datetime.timedelta(0, 60)
132
    >>> string_to_delta('60m')
133
    datetime.timedelta(0, 3600)
134
    >>> string_to_delta('24h')
135
    datetime.timedelta(1)
136
    >>> string_to_delta(u'1w')
137
    datetime.timedelta(7)
138
    """
139
    try:
140
        suffix=unicode(interval[-1])
141
        value=int(interval[:-1])
142
        if suffix == u"d":
143
            delta = datetime.timedelta(value)
144
        elif suffix == u"s":
145
            delta = datetime.timedelta(0, value)
146
        elif suffix == u"m":
147
            delta = datetime.timedelta(0, 0, 0, 0, value)
148
        elif suffix == u"h":
149
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
150
        elif suffix == u"w":
151
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
152
        else:
153
            raise ValueError
154
    except (ValueError, IndexError):
155
        raise ValueError
156
    return delta
157
3 by Björn Påhlsson
Python based server
158
def main():
159
    parser = OptionParser()
160
    parser.add_option("-i", "--interface", type="string",
161
                      default="eth0", metavar="IF",
162
                      help="Interface to bind to")
163
    parser.add_option("--cert", type="string", default="cert.pem",
164
                      metavar="FILE",
165
                      help="Public key certificate to use")
166
    parser.add_option("--key", type="string", default="key.pem",
167
                      metavar="FILE",
168
                      help="Private key to use")
169
    parser.add_option("--ca", type="string", default="ca.pem",
170
                      metavar="FILE",
171
                      help="Certificate Authority certificate to use")
172
    parser.add_option("--crl", type="string", default="crl.pem",
173
                      metavar="FILE",
174
                      help="Certificate Revokation List to use")
175
    parser.add_option("-p", "--port", type="int", default=49001,
176
                      help="Port number to receive requests on")
177
    parser.add_option("--dh", type="int", metavar="BITS",
178
                      help="DH group to use")
179
    parser.add_option("-t", "--timeout", type="string", # Parsed later
180
                      default="15m",
181
                      help="Amount of downtime allowed for clients")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
182
    parser.add_option("--interval", type="string", # Parsed later
183
                      default="5m",
184
                      help="How often to check that a client is up")
185
    parser.add_option("--check", action="store_true", default=False,
186
                      help="Run self-test")
3 by Björn Påhlsson
Python based server
187
    (options, args) = parser.parse_args()
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
188
189
    if options.check:
190
        import doctest
191
        doctest.testmod()
192
        sys.exit()
3 by Björn Påhlsson
Python based server
193
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
194
    # Parse the time arguments
3 by Björn Påhlsson
Python based server
195
    try:
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
196
        options.timeout = string_to_delta(options.timeout)
197
    except ValueError:
3 by Björn Påhlsson
Python based server
198
        parser.error("option --timeout: Unparseable time")
199
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
200
    try:
201
        options.interval = string_to_delta(options.interval)
202
    except ValueError:
203
        parser.error("option --interval: Unparseable time")
204
    
3 by Björn Påhlsson
Python based server
205
    cert = gnutls.crypto.X509Certificate(open(options.cert).read())
206
    key = gnutls.crypto.X509PrivateKey(open(options.key).read())
207
    ca = gnutls.crypto.X509Certificate(open(options.ca).read())
208
    crl = gnutls.crypto.X509CRL(open(options.crl).read())
209
    cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl])
210
    
211
    # Parse config file
212
    defaults = {}
213
    client_config_object = ConfigParser.SafeConfigParser(defaults)
214
    client_config_object.read("mandos-clients.conf")
215
    clients = [Client(name=section,
216
                      **(dict(client_config_object.items(section))))
217
               for section in client_config_object.sections()]
218
    
219
    udp_server = IPv6_UDPServer((in6addr_any, options.port),
220
                                udp_handler,
221
                                options=options)
222
    
223
    tcp_server = IPv6_TCPServer((in6addr_any, options.port),
224
                                tcp_handler,
225
                                options=options,
226
                                clients=clients,
227
                                credentials=cred)
228
    
229
    while True:
230
        in_, out, err = select.select((udp_server,
231
                                       tcp_server), (), ())
232
        for server in in_:
233
            server.handle_request()
234
235
236
if __name__ == "__main__":
237
    main()
238