]> git.ipfire.org Git - thirdparty/hostap.git/blob - hostapd/wps-ap-nfc.py
HE: Add HE channel management configuration options
[thirdparty/hostap.git] / hostapd / wps-ap-nfc.py
1 #!/usr/bin/python
2 #
3 # Example nfcpy to hostapd wrapper for WPS NFC operations
4 # Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
5 #
6 # This software may be distributed under the terms of the BSD license.
7 # See README for more details.
8
9 import os
10 import sys
11 import time
12 import argparse
13
14 import nfc
15 import nfc.ndef
16 import nfc.llcp
17 import nfc.handover
18
19 import logging
20
21 import wpaspy
22
23 wpas_ctrl = '/var/run/hostapd'
24 continue_loop = True
25 summary_file = None
26 success_file = None
27
28 def summary(txt):
29 print(txt)
30 if summary_file:
31 with open(summary_file, 'a') as f:
32 f.write(txt + "\n")
33
34 def success_report(txt):
35 summary(txt)
36 if success_file:
37 with open(success_file, 'a') as f:
38 f.write(txt + "\n")
39
40 def wpas_connect():
41 ifaces = []
42 if os.path.isdir(wpas_ctrl):
43 try:
44 ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
45 except OSError as error:
46 print("Could not find hostapd: ", error)
47 return None
48
49 if len(ifaces) < 1:
50 print("No hostapd control interface found")
51 return None
52
53 for ctrl in ifaces:
54 try:
55 wpas = wpaspy.Ctrl(ctrl)
56 return wpas
57 except Exception as e:
58 pass
59 return None
60
61
62 def wpas_tag_read(message):
63 wpas = wpas_connect()
64 if (wpas == None):
65 return False
66 if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
67 return False
68 return True
69
70
71 def wpas_get_config_token():
72 wpas = wpas_connect()
73 if (wpas == None):
74 return None
75 ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
76 if "FAIL" in ret:
77 return None
78 return ret.rstrip().decode("hex")
79
80
81 def wpas_get_password_token():
82 wpas = wpas_connect()
83 if (wpas == None):
84 return None
85 ret = wpas.request("WPS_NFC_TOKEN NDEF")
86 if "FAIL" in ret:
87 return None
88 return ret.rstrip().decode("hex")
89
90
91 def wpas_get_handover_sel():
92 wpas = wpas_connect()
93 if (wpas == None):
94 return None
95 ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
96 if "FAIL" in ret:
97 return None
98 return ret.rstrip().decode("hex")
99
100
101 def wpas_report_handover(req, sel):
102 wpas = wpas_connect()
103 if (wpas == None):
104 return None
105 return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
106 str(req).encode("hex") + " " +
107 str(sel).encode("hex"))
108
109
110 class HandoverServer(nfc.handover.HandoverServer):
111 def __init__(self, llc):
112 super(HandoverServer, self).__init__(llc)
113 self.ho_server_processing = False
114 self.success = False
115
116 # override to avoid parser error in request/response.pretty() in nfcpy
117 # due to new WSC handover format
118 def _process_request(self, request):
119 summary("received handover request {}".format(request.type))
120 response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
121 if not request.type == 'urn:nfc:wkt:Hr':
122 summary("not a handover request")
123 else:
124 try:
125 request = nfc.ndef.HandoverRequestMessage(request)
126 except nfc.ndef.DecodeError as e:
127 summary("error decoding 'Hr' message: {}".format(e))
128 else:
129 response = self.process_request(request)
130 summary("send handover response {}".format(response.type))
131 return response
132
133 def process_request(self, request):
134 summary("HandoverServer - request received")
135 try:
136 print("Parsed handover request: " + request.pretty())
137 except Exception as e:
138 print(e)
139 print(str(request).encode("hex"))
140
141 sel = nfc.ndef.HandoverSelectMessage(version="1.2")
142
143 for carrier in request.carriers:
144 print("Remote carrier type: " + carrier.type)
145 if carrier.type == "application/vnd.wfa.wsc":
146 summary("WPS carrier type match - add WPS carrier record")
147 data = wpas_get_handover_sel()
148 if data is None:
149 summary("Could not get handover select carrier record from hostapd")
150 continue
151 print("Handover select carrier record from hostapd:")
152 print(data.encode("hex"))
153 if "OK" in wpas_report_handover(carrier.record, data):
154 success_report("Handover reported successfully")
155 else:
156 summary("Handover report rejected")
157
158 message = nfc.ndef.Message(data);
159 sel.add_carrier(message[0], "active", message[1:])
160
161 print("Handover select:")
162 try:
163 print(sel.pretty())
164 except Exception as e:
165 print(e)
166 print(str(sel).encode("hex"))
167
168 summary("Sending handover select")
169 self.success = True
170 return sel
171
172
173 def wps_tag_read(tag):
174 success = False
175 if len(tag.ndef.message):
176 for record in tag.ndef.message:
177 print("record type " + record.type)
178 if record.type == "application/vnd.wfa.wsc":
179 summary("WPS tag - send to hostapd")
180 success = wpas_tag_read(tag.ndef.message)
181 break
182 else:
183 summary("Empty tag")
184
185 if success:
186 success_report("Tag read succeeded")
187
188 return success
189
190
191 def rdwr_connected_write(tag):
192 summary("Tag found - writing - " + str(tag))
193 global write_data
194 tag.ndef.message = str(write_data)
195 success_report("Tag write succeeded")
196 print("Done - remove tag")
197 global only_one
198 if only_one:
199 global continue_loop
200 continue_loop = False
201 global write_wait_remove
202 while write_wait_remove and tag.is_present:
203 time.sleep(0.1)
204
205 def wps_write_config_tag(clf, wait_remove=True):
206 summary("Write WPS config token")
207 global write_data, write_wait_remove
208 write_wait_remove = wait_remove
209 write_data = wpas_get_config_token()
210 if write_data == None:
211 summary("Could not get WPS config token from hostapd")
212 return
213
214 print("Touch an NFC tag")
215 clf.connect(rdwr={'on-connect': rdwr_connected_write})
216
217
218 def wps_write_password_tag(clf, wait_remove=True):
219 summary("Write WPS password token")
220 global write_data, write_wait_remove
221 write_wait_remove = wait_remove
222 write_data = wpas_get_password_token()
223 if write_data == None:
224 summary("Could not get WPS password token from hostapd")
225 return
226
227 print("Touch an NFC tag")
228 clf.connect(rdwr={'on-connect': rdwr_connected_write})
229
230
231 def rdwr_connected(tag):
232 global only_one, no_wait
233 summary("Tag connected: " + str(tag))
234
235 if tag.ndef:
236 print("NDEF tag: " + tag.type)
237 try:
238 print(tag.ndef.message.pretty())
239 except Exception as e:
240 print(e)
241 success = wps_tag_read(tag)
242 if only_one and success:
243 global continue_loop
244 continue_loop = False
245 else:
246 summary("Not an NDEF tag - remove tag")
247 return True
248
249 return not no_wait
250
251
252 def llcp_startup(clf, llc):
253 print("Start LLCP server")
254 global srv
255 srv = HandoverServer(llc)
256 return llc
257
258 def llcp_connected(llc):
259 print("P2P LLCP connected")
260 global wait_connection
261 wait_connection = False
262 global srv
263 srv.start()
264 return True
265
266
267 def main():
268 clf = nfc.ContactlessFrontend()
269
270 parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
271 parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
272 action='store_const', dest='loglevel',
273 help='verbose debug output')
274 parser.add_argument('-q', const=logging.WARNING, action='store_const',
275 dest='loglevel', help='be quiet')
276 parser.add_argument('--only-one', '-1', action='store_true',
277 help='run only one operation and exit')
278 parser.add_argument('--no-wait', action='store_true',
279 help='do not wait for tag to be removed before exiting')
280 parser.add_argument('--summary',
281 help='summary file for writing status updates')
282 parser.add_argument('--success',
283 help='success file for writing success update')
284 parser.add_argument('command', choices=['write-config',
285 'write-password'],
286 nargs='?')
287 args = parser.parse_args()
288
289 global only_one
290 only_one = args.only_one
291
292 global no_wait
293 no_wait = args.no_wait
294
295 if args.summary:
296 global summary_file
297 summary_file = args.summary
298
299 if args.success:
300 global success_file
301 success_file = args.success
302
303 logging.basicConfig(level=args.loglevel)
304
305 try:
306 if not clf.open("usb"):
307 print("Could not open connection with an NFC device")
308 raise SystemExit
309
310 if args.command == "write-config":
311 wps_write_config_tag(clf, wait_remove=not args.no_wait)
312 raise SystemExit
313
314 if args.command == "write-password":
315 wps_write_password_tag(clf, wait_remove=not args.no_wait)
316 raise SystemExit
317
318 global continue_loop
319 while continue_loop:
320 print("Waiting for a tag or peer to be touched")
321 wait_connection = True
322 try:
323 if not clf.connect(rdwr={'on-connect': rdwr_connected},
324 llcp={'on-startup': llcp_startup,
325 'on-connect': llcp_connected}):
326 break
327 except Exception as e:
328 print("clf.connect failed")
329
330 global srv
331 if only_one and srv and srv.success:
332 raise SystemExit
333
334 except KeyboardInterrupt:
335 raise SystemExit
336 finally:
337 clf.close()
338
339 raise SystemExit
340
341 if __name__ == '__main__':
342 main()