1 /*
2 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
3 */
4 #include "wifi_provision_internal.h"
5
6 #ifdef AWSS_SUPPORT_SMARTCONFIG_WPS
7
8 #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
9 extern "C" {
10 #endif
11
is_ascii_string(uint8_t * str)12 int is_ascii_string(uint8_t *str)
13 {
14 int i = 0;
15 while (str[i] != '\0') {
16 if (str[i] < 128) {
17 i++;
18 } else {
19 return 0;
20 }
21 }
22 return 1;
23 }
24
25 /**
26 * extract device name attribute from wps ie struct
27 *
28 * @wps_ie: [IN] wps ie struct
29 * @len: [OUT] len of dev name attr if exist
30 *
31 * Return:
32 * %NULL if dev name attr could not be found, otherwise return a
33 * pointer to dev name attr
34 */
get_device_name_attr_from_wps(uint8_t * wps_ie,uint8_t * len)35 static uint8_t *get_device_name_attr_from_wps(uint8_t *wps_ie, uint8_t *len)
36 {
37 /* 6 = 1(Element ID) + 1(Length) + 4(WPS OUI) */
38 uint8_t *attr_ptr = wps_ie + 6; /* goto first attr */
39 uint8_t wps_ielen = wps_ie[1];
40
41 #define device_name_id (0x1011)
42 while (attr_ptr - wps_ie < wps_ielen) {
43 /* 4 = 2(Attribute ID) + 2(Length) */
44 uint16_t attr_id = os_get_unaligned_be16(attr_ptr);
45 uint16_t attr_data_len = os_get_unaligned_be16(attr_ptr + 2);
46 uint16_t attr_len = attr_data_len + 4;
47
48 if (attr_id == device_name_id) {
49 *len = attr_len;
50 return attr_ptr;
51 } else {
52 attr_ptr += attr_len; /* goto next */
53 }
54 }
55 return NULL;
56 }
57
58 /*
59 * passwd_check_utf8()
60 *
61 * @Note: see andriod smartconfig with p2p
62 * if the byte in passwd is zero, jave will change 0x00 with 0xc080
63 * the function will restore 0xc080 to 0x00
64 */
passwd_check_utf8(uint8_t * passwd,int * passwd_len)65 void passwd_check_utf8(uint8_t *passwd, int *passwd_len)
66 {
67 int i, len;
68
69 if (!passwd || !passwd_len) {
70 return;
71 }
72
73 len = *passwd_len;
74 for (i = 0; i < len; i++) {
75 if (passwd[i] < 0x80) { /* [0x01 ~ 0x7F] */
76 continue;
77 }
78 passwd[i] = 0; /* resetore to 0x00 */
79 if (i + 2 < len) { /* move the rest to overwrite useless content. */
80 memmove(passwd + i + 1, passwd + i + 2, len - i - 2);
81 }
82 len--;
83 }
84 *passwd_len = len;
85 }
86
87 /*
88 * get_ssid_passwd_from_wps()
89 *
90 * @Note: see andriod zconfig protocol
91 * android sdk limit sizeof(passwd) <= 23
92 *
93 * @Return: _GOT_RESULT_
94 *
95 * use src mac to do ssid completion
96 */
get_ssid_passwd_from_w(uint8_t * in,int total_len,uint8_t * src,uint8_t * bssid)97 static int get_ssid_passwd_from_w(uint8_t *in, int total_len, uint8_t *src,
98 uint8_t *bssid)
99 {
100 uint8_t tmp_ssid[ZC_MAX_SSID_LEN + 1] = { 0 },
101 tmp_passwd[ZC_MAX_PASSWD_LEN + 1] = {
102 0
103 };
104 int ssid_len, passwd_len, ssid_truncated = 0;
105 uint16_t crc, cal_crc;
106 char encrypt = 0;
107 /* used by prepare frame */
108 char *magic_p_w_d =
109 "zl&ws"; /* FIXME: Maybe it will be dangerous when opening source. */
110 static uint32_t start_time = 0;
111
112 #define W_LEN (32) /* total_len */
113 #define EXTRA_LEN (3) /* ssid_len(1B) + checksum(2B) */
114 if (!in || total_len <= 4 + EXTRA_LEN) {
115 return GOT_NOTHING;
116 }
117
118 /* attr_id(2) + attr_len(2) = 4 */
119 in += 4;
120 total_len -= 4;
121
122 if (total_len > W_LEN) {
123 awss_warn("ssid len > 32\r\n");
124 }
125
126 /* total_len: ssid_len(1B), ssid, passwd, crc(2B) */
127 ssid_len = in[0];
128 if (ssid_len & P2P_ENCRYPT_BIT_MASK) {
129 encrypt = (ssid_len & P2P_ENCRYPT_BIT_MASK) >> P2P_ENCODE_TYPE_OFFSET;
130 ssid_len &= P2P_SSID_LEN_MASK;
131 }
132 if (encrypt > P2P_ENCODE_TYPE_ENCRYPT) {
133 return GOT_NOTHING;
134 }
135
136 passwd_len = total_len - ssid_len - EXTRA_LEN; /* ssid_len(1B), crc(2B) */
137 if (ssid_len > W_LEN - EXTRA_LEN || passwd_len < 0) {
138 return GOT_NOTHING;
139 }
140
141 AWSS_UPDATE_STATIS(AWSS_STATIS_WPS_IDX, AWSS_STATIS_TYPE_TIME_START);
142 /* ssid_len(1B), ssid, passwd, crc(2B) */
143 crc = os_get_unaligned_be16(in + 1 + ssid_len + passwd_len);
144 /* restore 0xc080 to 0x00 */
145 passwd_check_utf8(in + 1 + ssid_len, &passwd_len);
146 cal_crc = zconfig_checksum_v3(in, 1 + ssid_len + passwd_len);
147 if (crc != cal_crc) {
148 memset(zc_android_src, 0, sizeof(zconfig_data->android_src));
149 memset(zc_pre_ssid, 0, sizeof(zconfig_data->android_pre_ssid));
150 memset(zc_android_ssid, 0, sizeof(zconfig_data->android_ssid));
151 memset(zc_android_bssid, 0, sizeof(zconfig_data->android_bssid));
152 awss_debug("rx illegal p2p (0x%x != 0x%x)\r\n", crc, cal_crc);
153 awss_event_post(IOTX_AWSS_CS_ERR);
154 AWSS_UPDATE_STATIS(AWSS_STATIS_WPS_IDX, AWSS_STATIS_TYPE_CRC_ERR);
155 /*
156 * use zconfig_checksum_v3() because
157 * java modified UTF-8, U+C080 equal U+00,
158 * ssid len & ssid & crc is not be 0,
159 * the content of passwd encrypted may be 0
160 */
161 return GOT_NOTHING;
162 }
163
164 if (start_time == 0) {
165 start_time = os_get_time_ms();
166 }
167
168 #define MAC_LOCAL_ADMINISTERED_BIT (0x02)
169 memcpy(zc_android_src, src, ETH_ALEN);
170 if (zc_android_src[0] & MAC_LOCAL_ADMINISTERED_BIT) {
171 zc_android_src[0] &= ~MAC_LOCAL_ADMINISTERED_BIT;
172 /*awss_debug("android src: %02x%02x%02x\r\n", zc_android_src[0], src[1],
173 * src[2]); */
174 } else {
175 awss_warn("local administered bit not set: %02x%02x%02x\r\n", src[0],
176 src[1], src[2]);
177 }
178
179 in += 1; /* eating ssid_len(1B) */
180
181 memset(tmp_ssid, 0, ZC_MAX_SSID_LEN);
182 memset(tmp_passwd, 0, ZC_MAX_PASSWD_LEN);
183
184 memcpy(tmp_ssid, in, ssid_len);
185 in += ssid_len;
186 if (passwd_len) {
187 memcpy(tmp_passwd, in, passwd_len);
188 }
189
190 awss_dict_crypt(SSID_DECODE_TABLE, tmp_ssid, ssid_len);
191
192 switch (encrypt) {
193 case P2P_ENCODE_TYPE_ENCRYPT:
194 {
195 /* decypt passwd using aes128-cfb */
196 uint8_t passwd_cipher_len = 0;
197 uint8_t *passwd_cipher = awss_zalloc(128);
198 if (passwd_cipher == NULL) {
199 return GOT_NOTHING;
200 }
201
202 decode_chinese(tmp_passwd, passwd_len, passwd_cipher,
203 &passwd_cipher_len, 7);
204 passwd_len = passwd_cipher_len;
205 memset(tmp_passwd, 0, ZC_MAX_PASSWD_LEN);
206 aes_decrypt_string((char *)passwd_cipher, (char *)tmp_passwd,
207 passwd_len, 1, awss_get_encrypt_type(), 0, NULL);
208 HAL_Free(passwd_cipher);
209 if (is_utf8((const char *)tmp_passwd, passwd_len) == 0) {
210 /* memset(zconfig_data, 0, sizeof(*zconfig_data)); */
211 memset(zc_android_src, 0, sizeof(zconfig_data->android_src));
212 memset(zc_pre_ssid, 0, sizeof(zconfig_data->android_pre_ssid));
213 memset(zc_android_ssid, 0, sizeof(zconfig_data->android_ssid));
214 memset(zc_android_bssid, 0,
215 sizeof(zconfig_data->android_bssid));
216
217 awss_warn("p2p decrypt passwd content err\r\n");
218 awss_event_post(IOTX_AWSS_PASSWD_ERR);
219 AWSS_UPDATE_STATIS(AWSS_STATIS_WPS_IDX,
220 AWSS_STATIS_TYPE_PASSWD_ERR);
221 return GOT_NOTHING;
222 }
223 break;
224 }
225 default:
226 {
227 void *temp_mutex = zc_mutex;
228 awss_warn("p2p encypt:%d not support\r\n", encrypt);
229 memset(zconfig_data, 0, sizeof(*zconfig_data));
230 zc_mutex = temp_mutex;
231 return GOT_NOTHING;
232 }
233 }
234
235 awss_debug("ssid:%s, tlen:%d\r\n", tmp_ssid, total_len);
236 if (passwd_len && !memcmp(tmp_passwd, magic_p_w_d, passwd_len)) {
237 /* Note: when v2 rollback to v1, zc_preapre_ssid will useless */
238 strncpy((char *)zc_pre_ssid, (char const *)tmp_ssid,
239 ZC_MAX_SSID_LEN - 1);
240 return GOT_CHN_LOCK;
241 }
242 /*
243 // for ascii ssid, max length is 29(32 - 1 - 2).
244 // for utf-8 ssid, max length is 29 - 2 or 29 - 3
245 // gbk ssid also encoded as utf-8
246 // SAMSUNG S4 max name length = 22
247 */
248 if (!is_ascii_string((uint8_t *)tmp_ssid)) { /* chinese ssid */
249 ssid_truncated = 1; /* in case of gbk chinese */
250 } else if (total_len >= W_LEN - EXTRA_LEN) {
251 ssid_truncated = 1;
252 }
253
254 if (ssid_truncated) {
255 uint8_t *best_ssid;
256 int cur_ssid_len = strlen((const char *)tmp_ssid); /* current_ssid */
257 int pre_ssid_len = strlen((const char *)zc_pre_ssid); /* prepare_ssid */
258 if (pre_ssid_len && pre_ssid_len < cur_ssid_len) {
259 /* should not happen */
260 awss_warn("pre:%s < cur:%s\r\n", zc_pre_ssid, tmp_ssid);
261 best_ssid = tmp_ssid; /* current ssid */
262 } else if (pre_ssid_len) {
263 best_ssid = zc_pre_ssid; /* prepare ssid */
264 } else {
265 best_ssid = tmp_ssid; /* default use current ssid */
266 }
267
268 /* awss_debug("ssid truncated, best ssid: %s\r\n", best_ssid); */
269
270 do {
271 #ifdef AWSS_SUPPORT_APLIST
272 struct ap_info *ap_info;
273 ap_info = zconfig_get_apinfo_by_ssid_suffix(best_ssid);
274 if (ap_info) {
275 awss_debug("ssid truncated, got ssid from aplist:%s\r\n",
276 best_ssid);
277 strncpy((char *)zc_ssid, (const char *)ap_info->ssid,
278 ZC_MAX_SSID_LEN - 1);
279 memcpy(zc_bssid, ap_info->mac, ETH_ALEN);
280 } else
281 #endif
282 {
283 if (memcmp(bssid, zero_mac, ETH_ALEN) &&
284 memcmp(bssid, br_mac, ETH_ALEN)) {
285 memcpy(zc_android_bssid, bssid, ETH_ALEN);
286 }
287 #ifdef AWSS_SUPPORT_APLIST
288 ap_info = zconfig_get_apinfo(zc_android_bssid);
289 if (ap_info) {
290 if (ap_info->ssid[0] ==
291 '\0') { /* hide ssid, MUST not truncate */
292 strncpy((char *)zc_android_ssid,
293 (const char *)best_ssid, ZC_MAX_SSID_LEN - 1);
294 } else { /* not hide ssid, amend ssid according to ap list
295 */
296 strncpy((char *)zc_android_ssid,
297 (const char *)ap_info->ssid,
298 ZC_MAX_SSID_LEN - 1);
299 }
300 } else
301 #endif
302 if (time_elapsed_ms_since(start_time) >
303 awss_get_channel_scan_interval_ms() * (13 + 3) * 2) {
304 start_time = 0;
305 strncpy((char *)zc_android_ssid, (const char *)best_ssid,
306 ZC_MAX_SSID_LEN - 1);
307 }
308
309 if (zc_android_ssid[0] == '\0') {
310 return GOT_NOTHING;
311 }
312 strncpy((char *)zc_ssid, (const char *)zc_android_ssid,
313 ZC_MAX_SSID_LEN - 1);
314 memcpy(zc_bssid, zc_android_bssid, ETH_ALEN);
315 }
316 } while (0);
317 } else {
318 strncpy((char *)zc_ssid, (char const *)tmp_ssid, ZC_MAX_SSID_LEN - 1);
319 if (memcmp(bssid, zero_mac, ETH_ALEN) &&
320 memcmp(bssid, br_mac, ETH_ALEN)) {
321 memcpy(zc_bssid, bssid, ETH_ALEN);
322 }
323 }
324
325 strncpy((char *)zc_passwd, (char const *)tmp_passwd, ZC_MAX_PASSWD_LEN - 1);
326 start_time = 0;
327
328 return GOT_SSID_PASSWD;
329 }
330
awss_recv_callback_wps(struct parser_res * res)331 int awss_recv_callback_wps(struct parser_res *res)
332 {
333 uint8_t *data = res->u.wps.data;
334 uint16_t len = res->u.wps.data_len;
335
336 uint8_t tods = res->tods;
337 uint8_t channel = res->channel;
338
339 int ret = get_ssid_passwd_from_w(data, len, res->src, res->bssid);
340 if (ret == GOT_CHN_LOCK) {
341 awss_debug("callback for v2:%02x%02x%02x\r\n", res->src[0], res->src[1],
342 res->src[2]);
343 goto chn_locked;
344 } else if (ret == GOT_SSID_PASSWD) {
345 goto rcv_done;
346 } else if (ret == GOT_NOTHING) {
347 return PKG_INVALID;
348 } else {
349 return PKG_INVALID;
350 }
351
352 chn_locked:
353 zconfig_set_state(STATE_CHN_LOCKED_BY_P2P, tods, channel);
354 return PKG_START_FRAME;
355 rcv_done:
356 AWSS_UPDATE_STATIS(AWSS_STATIS_WPS_IDX, AWSS_STATIS_TYPE_TIME_SUC);
357 zconfig_set_state(STATE_RCV_DONE, tods, channel);
358 return PKG_END;
359 }
360
awss_ieee80211_wps_process(uint8_t * mgmt_header,int len,int link_type,struct parser_res * res,signed char rssi)361 int awss_ieee80211_wps_process(uint8_t *mgmt_header, int len, int link_type,
362 struct parser_res *res, signed char rssi)
363 {
364 const uint8_t *wps_ie = NULL;
365 struct ieee80211_hdr *hdr;
366 uint8_t attr_len = 0;
367 uint16_t ieoffset;
368 int fc;
369
370 /*
371 * when device try to connect current router (include aha)
372 * skip the wps packet.
373 */
374 if (mgmt_header == NULL || zconfig_finished) {
375 return ALINK_INVALID;
376 }
377
378 /*
379 * we don't process wps until user press configure button
380 */
381 if (awss_get_config_press() == 0) {
382 return ALINK_INVALID;
383 }
384
385 hdr = (struct ieee80211_hdr *)mgmt_header;
386 fc = hdr->frame_control;
387
388 if (!ieee80211_is_probe_req(fc)) {
389 return ALINK_INVALID;
390 }
391
392 ieoffset = offsetof(struct ieee80211_mgmt, u.probe_req.variable);
393 if (ieoffset > len) {
394 return ALINK_INVALID;
395 }
396 /* get wps ie */
397 wps_ie = (const uint8_t *)cfg80211_find_vendor_ie(
398 WLAN_OUI_WPS, WLAN_OUI_TYPE_WPS, mgmt_header + ieoffset,
399 len - ieoffset);
400 if (wps_ie == NULL) {
401 return ALINK_INVALID;
402 }
403 /* get wps name in wps ie */
404 wps_ie = (const uint8_t *)get_device_name_attr_from_wps((uint8_t *)wps_ie,
405 &attr_len);
406 if (wps_ie == NULL) {
407 return ALINK_INVALID;
408 }
409 res->u.wps.data_len = attr_len;
410 res->u.wps.data = (uint8_t *)wps_ie;
411 return ALINK_WPS;
412 }
413 #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
414 }
415 #endif
416
417 #endif
418