1*a1157835SDaniel Fojt#!/usr/bin/env python2
2*a1157835SDaniel Fojt#
3*a1157835SDaniel Fojt# eapol_test controller
4*a1157835SDaniel Fojt# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
5*a1157835SDaniel Fojt#
6*a1157835SDaniel Fojt# This software may be distributed under the terms of the BSD license.
7*a1157835SDaniel Fojt# See README for more details.
8*a1157835SDaniel Fojt
9*a1157835SDaniel Fojtimport argparse
10*a1157835SDaniel Fojtimport logging
11*a1157835SDaniel Fojtimport os
12*a1157835SDaniel Fojtimport Queue
13*a1157835SDaniel Fojtimport sys
14*a1157835SDaniel Fojtimport threading
15*a1157835SDaniel Fojt
16*a1157835SDaniel Fojtlogger = logging.getLogger()
17*a1157835SDaniel Fojtdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
18*a1157835SDaniel Fojtsys.path.append(os.path.join(dir, '..', 'wpaspy'))
19*a1157835SDaniel Fojtimport wpaspy
20*a1157835SDaniel Fojtwpas_ctrl = '/tmp/eapol_test'
21*a1157835SDaniel Fojt
22*a1157835SDaniel Fojtclass eapol_test:
23*a1157835SDaniel Fojt    def __init__(self, ifname):
24*a1157835SDaniel Fojt        self.ifname = ifname
25*a1157835SDaniel Fojt        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
26*a1157835SDaniel Fojt        if "PONG" not in self.ctrl.request("PING"):
27*a1157835SDaniel Fojt            raise Exception("Failed to connect to eapol_test (%s)" % ifname)
28*a1157835SDaniel Fojt        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
29*a1157835SDaniel Fojt        self.mon.attach()
30*a1157835SDaniel Fojt
31*a1157835SDaniel Fojt    def add_network(self):
32*a1157835SDaniel Fojt        id = self.request("ADD_NETWORK")
33*a1157835SDaniel Fojt        if "FAIL" in id:
34*a1157835SDaniel Fojt            raise Exception("ADD_NETWORK failed")
35*a1157835SDaniel Fojt        return int(id)
36*a1157835SDaniel Fojt
37*a1157835SDaniel Fojt    def remove_network(self, id):
38*a1157835SDaniel Fojt        id = self.request("REMOVE_NETWORK " + str(id))
39*a1157835SDaniel Fojt        if "FAIL" in id:
40*a1157835SDaniel Fojt            raise Exception("REMOVE_NETWORK failed")
41*a1157835SDaniel Fojt        return None
42*a1157835SDaniel Fojt
43*a1157835SDaniel Fojt    def set_network(self, id, field, value):
44*a1157835SDaniel Fojt        res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
45*a1157835SDaniel Fojt        if "FAIL" in res:
46*a1157835SDaniel Fojt            raise Exception("SET_NETWORK failed")
47*a1157835SDaniel Fojt        return None
48*a1157835SDaniel Fojt
49*a1157835SDaniel Fojt    def set_network_quoted(self, id, field, value):
50*a1157835SDaniel Fojt        res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
51*a1157835SDaniel Fojt        if "FAIL" in res:
52*a1157835SDaniel Fojt            raise Exception("SET_NETWORK failed")
53*a1157835SDaniel Fojt        return None
54*a1157835SDaniel Fojt
55*a1157835SDaniel Fojt    def request(self, cmd, timeout=10):
56*a1157835SDaniel Fojt        return self.ctrl.request(cmd, timeout=timeout)
57*a1157835SDaniel Fojt
58*a1157835SDaniel Fojt    def wait_event(self, events, timeout=10):
59*a1157835SDaniel Fojt        start = os.times()[4]
60*a1157835SDaniel Fojt        while True:
61*a1157835SDaniel Fojt            while self.mon.pending():
62*a1157835SDaniel Fojt                ev = self.mon.recv()
63*a1157835SDaniel Fojt                logger.debug(self.ifname + ": " + ev)
64*a1157835SDaniel Fojt                for event in events:
65*a1157835SDaniel Fojt                    if event in ev:
66*a1157835SDaniel Fojt                        return ev
67*a1157835SDaniel Fojt            now = os.times()[4]
68*a1157835SDaniel Fojt            remaining = start + timeout - now
69*a1157835SDaniel Fojt            if remaining <= 0:
70*a1157835SDaniel Fojt                break
71*a1157835SDaniel Fojt            if not self.mon.pending(timeout=remaining):
72*a1157835SDaniel Fojt                break
73*a1157835SDaniel Fojt        return None
74*a1157835SDaniel Fojt
75*a1157835SDaniel Fojtdef run(ifname, count, no_fast_reauth, res):
76*a1157835SDaniel Fojt    et = eapol_test(ifname)
77*a1157835SDaniel Fojt
78*a1157835SDaniel Fojt    et.request("AP_SCAN 0")
79*a1157835SDaniel Fojt    if no_fast_reauth:
80*a1157835SDaniel Fojt        et.request("SET fast_reauth 0")
81*a1157835SDaniel Fojt    else:
82*a1157835SDaniel Fojt        et.request("SET fast_reauth 1")
83*a1157835SDaniel Fojt    id = et.add_network()
84*a1157835SDaniel Fojt    et.set_network(id, "key_mgmt", "IEEE8021X")
85*a1157835SDaniel Fojt    et.set_network(id, "eapol_flags", "0")
86*a1157835SDaniel Fojt    et.set_network(id, "eap", "TLS")
87*a1157835SDaniel Fojt    et.set_network_quoted(id, "identity", "user")
88*a1157835SDaniel Fojt    et.set_network_quoted(id, "ca_cert", 'ca.pem')
89*a1157835SDaniel Fojt    et.set_network_quoted(id, "client_cert", 'client.pem')
90*a1157835SDaniel Fojt    et.set_network_quoted(id, "private_key", 'client.key')
91*a1157835SDaniel Fojt    et.set_network_quoted(id, "private_key_passwd", 'whatever')
92*a1157835SDaniel Fojt    et.set_network(id, "disabled", "0")
93*a1157835SDaniel Fojt
94*a1157835SDaniel Fojt    fail = False
95*a1157835SDaniel Fojt    for i in range(count):
96*a1157835SDaniel Fojt        et.request("REASSOCIATE")
97*a1157835SDaniel Fojt        ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
98*a1157835SDaniel Fojt        if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
99*a1157835SDaniel Fojt            fail = True
100*a1157835SDaniel Fojt            break
101*a1157835SDaniel Fojt
102*a1157835SDaniel Fojt    et.remove_network(id)
103*a1157835SDaniel Fojt
104*a1157835SDaniel Fojt    if fail:
105*a1157835SDaniel Fojt        res.put("FAIL (%d OK)" % i)
106*a1157835SDaniel Fojt    else:
107*a1157835SDaniel Fojt        res.put("PASS %d" % (i + 1))
108*a1157835SDaniel Fojt
109*a1157835SDaniel Fojtdef main():
110*a1157835SDaniel Fojt    parser = argparse.ArgumentParser(description='eapol_test controller')
111*a1157835SDaniel Fojt    parser.add_argument('--ctrl', help='control interface directory')
112*a1157835SDaniel Fojt    parser.add_argument('--num', help='number of processes')
113*a1157835SDaniel Fojt    parser.add_argument('--iter', help='number of iterations')
114*a1157835SDaniel Fojt    parser.add_argument('--no-fast-reauth', action='store_true',
115*a1157835SDaniel Fojt                        dest='no_fast_reauth',
116*a1157835SDaniel Fojt                        help='disable TLS session resumption')
117*a1157835SDaniel Fojt    args = parser.parse_args()
118*a1157835SDaniel Fojt
119*a1157835SDaniel Fojt    num = int(args.num)
120*a1157835SDaniel Fojt    iter = int(args.iter)
121*a1157835SDaniel Fojt    if args.ctrl:
122*a1157835SDaniel Fojt        global wpas_ctrl
123*a1157835SDaniel Fojt        wpas_ctrl = args.ctrl
124*a1157835SDaniel Fojt
125*a1157835SDaniel Fojt    t = {}
126*a1157835SDaniel Fojt    res = {}
127*a1157835SDaniel Fojt    for i in range(num):
128*a1157835SDaniel Fojt        res[i] = Queue.Queue()
129*a1157835SDaniel Fojt        t[i] = threading.Thread(target=run, args=(str(i), iter,
130*a1157835SDaniel Fojt                                                  args.no_fast_reauth, res[i]))
131*a1157835SDaniel Fojt    for i in range(num):
132*a1157835SDaniel Fojt        t[i].start()
133*a1157835SDaniel Fojt    for i in range(num):
134*a1157835SDaniel Fojt        t[i].join()
135*a1157835SDaniel Fojt        try:
136*a1157835SDaniel Fojt            results = res[i].get(False)
137*a1157835SDaniel Fojt        except:
138*a1157835SDaniel Fojt            results = "N/A"
139*a1157835SDaniel Fojt        print("%d: %s" % (i, results))
140*a1157835SDaniel Fojt
141*a1157835SDaniel Fojtif __name__ == "__main__":
142*a1157835SDaniel Fojt    main()
143