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