1289284Srpaulo#!/usr/bin/env python2
2289284Srpaulo#
3289284Srpaulo# eapol_test controller
4289284Srpaulo# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
5289284Srpaulo#
6289284Srpaulo# This software may be distributed under the terms of the BSD license.
7289284Srpaulo# See README for more details.
8289284Srpaulo
9289284Srpauloimport argparse
10289284Srpauloimport logging
11289284Srpauloimport os
12289284Srpauloimport Queue
13289284Srpauloimport sys
14289284Srpauloimport threading
15289284Srpaulo
16289284Srpaulologger = logging.getLogger()
17289284Srpaulodir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
18289284Srpaulosys.path.append(os.path.join(dir, '..', 'wpaspy'))
19289284Srpauloimport wpaspy
20289284Srpaulowpas_ctrl = '/tmp/eapol_test'
21289284Srpaulo
22289284Srpauloclass eapol_test:
23289284Srpaulo    def __init__(self, ifname):
24289284Srpaulo        self.ifname = ifname
25289284Srpaulo        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
26289284Srpaulo        if "PONG" not in self.ctrl.request("PING"):
27289284Srpaulo            raise Exception("Failed to connect to eapol_test (%s)" % ifname)
28289284Srpaulo        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
29289284Srpaulo        self.mon.attach()
30289284Srpaulo
31289284Srpaulo    def add_network(self):
32289284Srpaulo        id = self.request("ADD_NETWORK")
33289284Srpaulo        if "FAIL" in id:
34289284Srpaulo            raise Exception("ADD_NETWORK failed")
35289284Srpaulo        return int(id)
36289284Srpaulo
37289284Srpaulo    def remove_network(self, id):
38289284Srpaulo        id = self.request("REMOVE_NETWORK " + str(id))
39289284Srpaulo        if "FAIL" in id:
40289284Srpaulo            raise Exception("REMOVE_NETWORK failed")
41289284Srpaulo        return None
42289284Srpaulo
43289284Srpaulo    def set_network(self, id, field, value):
44289284Srpaulo        res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
45289284Srpaulo        if "FAIL" in res:
46289284Srpaulo            raise Exception("SET_NETWORK failed")
47289284Srpaulo        return None
48289284Srpaulo
49289284Srpaulo    def set_network_quoted(self, id, field, value):
50289284Srpaulo        res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
51289284Srpaulo        if "FAIL" in res:
52289284Srpaulo            raise Exception("SET_NETWORK failed")
53289284Srpaulo        return None
54289284Srpaulo
55289284Srpaulo    def request(self, cmd, timeout=10):
56289284Srpaulo        return self.ctrl.request(cmd, timeout=timeout)
57289284Srpaulo
58289284Srpaulo    def wait_event(self, events, timeout=10):
59289284Srpaulo        start = os.times()[4]
60289284Srpaulo        while True:
61289284Srpaulo            while self.mon.pending():
62289284Srpaulo                ev = self.mon.recv()
63289284Srpaulo                logger.debug(self.ifname + ": " + ev)
64289284Srpaulo                for event in events:
65289284Srpaulo                    if event in ev:
66289284Srpaulo                        return ev
67289284Srpaulo            now = os.times()[4]
68289284Srpaulo            remaining = start + timeout - now
69289284Srpaulo            if remaining <= 0:
70289284Srpaulo                break
71289284Srpaulo            if not self.mon.pending(timeout=remaining):
72289284Srpaulo                break
73289284Srpaulo        return None
74289284Srpaulo
75289284Srpaulodef run(ifname, count, no_fast_reauth, res):
76289284Srpaulo    et = eapol_test(ifname)
77289284Srpaulo
78289284Srpaulo    et.request("AP_SCAN 0")
79289284Srpaulo    if no_fast_reauth:
80289284Srpaulo        et.request("SET fast_reauth 0")
81289284Srpaulo    else:
82289284Srpaulo        et.request("SET fast_reauth 1")
83289284Srpaulo    id = et.add_network()
84289284Srpaulo    et.set_network(id, "key_mgmt", "IEEE8021X")
85289284Srpaulo    et.set_network(id, "eapol_flags", "0")
86289284Srpaulo    et.set_network(id, "eap", "TLS")
87289284Srpaulo    et.set_network_quoted(id, "identity", "user")
88289284Srpaulo    et.set_network_quoted(id, "ca_cert", 'ca.pem')
89289284Srpaulo    et.set_network_quoted(id, "client_cert", 'client.pem')
90289284Srpaulo    et.set_network_quoted(id, "private_key", 'client.key')
91289284Srpaulo    et.set_network_quoted(id, "private_key_passwd", 'whatever')
92289284Srpaulo    et.set_network(id, "disabled", "0")
93289284Srpaulo
94289284Srpaulo    fail = False
95289284Srpaulo    for i in range(count):
96289284Srpaulo        et.request("REASSOCIATE")
97289284Srpaulo        ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
98289284Srpaulo        if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
99289284Srpaulo            fail = True
100289284Srpaulo            break
101289284Srpaulo
102289284Srpaulo    et.remove_network(id)
103289284Srpaulo
104289284Srpaulo    if fail:
105289284Srpaulo        res.put("FAIL (%d OK)" % i)
106289284Srpaulo    else:
107289284Srpaulo        res.put("PASS %d" % (i + 1))
108289284Srpaulo
109289284Srpaulodef main():
110289284Srpaulo    parser = argparse.ArgumentParser(description='eapol_test controller')
111289284Srpaulo    parser.add_argument('--ctrl', help='control interface directory')
112289284Srpaulo    parser.add_argument('--num', help='number of processes')
113289284Srpaulo    parser.add_argument('--iter', help='number of iterations')
114289284Srpaulo    parser.add_argument('--no-fast-reauth', action='store_true',
115289284Srpaulo                        dest='no_fast_reauth',
116289284Srpaulo                        help='disable TLS session resumption')
117289284Srpaulo    args = parser.parse_args()
118289284Srpaulo
119289284Srpaulo    num = int(args.num)
120289284Srpaulo    iter = int(args.iter)
121289284Srpaulo    if args.ctrl:
122289284Srpaulo        global wpas_ctrl
123289284Srpaulo        wpas_ctrl = args.ctrl
124289284Srpaulo
125289284Srpaulo    t = {}
126289284Srpaulo    res = {}
127289284Srpaulo    for i in range(num):
128289284Srpaulo        res[i] = Queue.Queue()
129289284Srpaulo        t[i] = threading.Thread(target=run, args=(str(i), iter,
130289284Srpaulo                                                  args.no_fast_reauth, res[i]))
131289284Srpaulo    for i in range(num):
132289284Srpaulo        t[i].start()
133289284Srpaulo    for i in range(num):
134289284Srpaulo        t[i].join()
135289284Srpaulo        try:
136289284Srpaulo            results = res[i].get(False)
137289284Srpaulo        except:
138289284Srpaulo            results = "N/A"
139289284Srpaulo        print "%d: %s" % (i, results)
140289284Srpaulo
141289284Srpauloif __name__ == "__main__":
142289284Srpaulo    main()
143