| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * Wi-Fi Protected Setup | 
 | 3 |  * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> | 
 | 4 |  * | 
| Dmitry Shmidt | c5ec7f5 | 2012-03-06 16:33:24 -0800 | [diff] [blame] | 5 |  * This software may be distributed under the terms of the BSD license. | 
 | 6 |  * See README for more details. | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 7 |  */ | 
 | 8 |  | 
 | 9 | #include "includes.h" | 
 | 10 |  | 
 | 11 | #include "common.h" | 
 | 12 | #include "crypto/dh_group5.h" | 
 | 13 | #include "common/ieee802_11_defs.h" | 
 | 14 | #include "wps_i.h" | 
 | 15 | #include "wps_dev_attr.h" | 
 | 16 |  | 
 | 17 |  | 
 | 18 | #ifdef CONFIG_WPS_TESTING | 
 | 19 | int wps_version_number = 0x20; | 
| Hai Shalom | a20dcd7 | 2022-02-04 13:43:00 -0800 | [diff] [blame] | 20 | int wps_testing_stub_cred = 0; | 
| Dmitry Shmidt | cf32e60 | 2014-01-28 10:57:39 -0800 | [diff] [blame] | 21 | int wps_corrupt_pkhash = 0; | 
| Dmitry Shmidt | de47be7 | 2016-01-07 12:52:55 -0800 | [diff] [blame] | 22 | int wps_force_auth_types_in_use = 0; | 
 | 23 | u16 wps_force_auth_types = 0; | 
 | 24 | int wps_force_encr_types_in_use = 0; | 
 | 25 | u16 wps_force_encr_types = 0; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 26 | #endif /* CONFIG_WPS_TESTING */ | 
 | 27 |  | 
 | 28 |  | 
 | 29 | /** | 
 | 30 |  * wps_init - Initialize WPS Registration protocol data | 
 | 31 |  * @cfg: WPS configuration | 
 | 32 |  * Returns: Pointer to allocated data or %NULL on failure | 
 | 33 |  * | 
 | 34 |  * This function is used to initialize WPS data for a registration protocol | 
 | 35 |  * instance (i.e., each run of registration protocol as a Registrar of | 
 | 36 |  * Enrollee. The caller is responsible for freeing this data after the | 
 | 37 |  * registration run has been completed by calling wps_deinit(). | 
 | 38 |  */ | 
 | 39 | struct wps_data * wps_init(const struct wps_config *cfg) | 
 | 40 | { | 
 | 41 | 	struct wps_data *data = os_zalloc(sizeof(*data)); | 
 | 42 | 	if (data == NULL) | 
 | 43 | 		return NULL; | 
 | 44 | 	data->wps = cfg->wps; | 
 | 45 | 	data->registrar = cfg->registrar; | 
 | 46 | 	if (cfg->registrar) { | 
 | 47 | 		os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN); | 
 | 48 | 	} else { | 
 | 49 | 		os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN); | 
 | 50 | 		os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); | 
 | 51 | 	} | 
 | 52 | 	if (cfg->pin) { | 
| Dmitry Shmidt | d5e4923 | 2012-12-03 15:08:10 -0800 | [diff] [blame] | 53 | 		data->dev_pw_id = cfg->dev_pw_id; | 
| Dmitry Shmidt | d2986c2 | 2017-10-23 14:22:09 -0700 | [diff] [blame] | 54 | 		data->dev_password = os_memdup(cfg->pin, cfg->pin_len); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 55 | 		if (data->dev_password == NULL) { | 
 | 56 | 			os_free(data); | 
 | 57 | 			return NULL; | 
 | 58 | 		} | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 59 | 		data->dev_password_len = cfg->pin_len; | 
| Dmitry Shmidt | f862328 | 2013-02-20 14:34:59 -0800 | [diff] [blame] | 60 | 		wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password", | 
 | 61 | 				data->dev_password, data->dev_password_len); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 62 | 	} | 
 | 63 |  | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 64 | #ifdef CONFIG_WPS_NFC | 
| Dmitry Shmidt | cf32e60 | 2014-01-28 10:57:39 -0800 | [diff] [blame] | 65 | 	if (cfg->pin == NULL && | 
 | 66 | 	    cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) | 
 | 67 | 		data->dev_pw_id = cfg->dev_pw_id; | 
 | 68 |  | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 69 | 	if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) { | 
| Dmitry Shmidt | f862328 | 2013-02-20 14:34:59 -0800 | [diff] [blame] | 70 | 		/* Keep AP PIN as alternative Device Password */ | 
 | 71 | 		data->alt_dev_pw_id = data->dev_pw_id; | 
 | 72 | 		data->alt_dev_password = data->dev_password; | 
 | 73 | 		data->alt_dev_password_len = data->dev_password_len; | 
 | 74 |  | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 75 | 		data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 76 | 		data->dev_password = | 
| Dmitry Shmidt | d2986c2 | 2017-10-23 14:22:09 -0700 | [diff] [blame] | 77 | 			os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw), | 
 | 78 | 				  wpabuf_len(cfg->wps->ap_nfc_dev_pw)); | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 79 | 		if (data->dev_password == NULL) { | 
 | 80 | 			os_free(data); | 
 | 81 | 			return NULL; | 
 | 82 | 		} | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 83 | 		data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); | 
| Dmitry Shmidt | f862328 | 2013-02-20 14:34:59 -0800 | [diff] [blame] | 84 | 		wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password", | 
 | 85 | 			    data->dev_password, data->dev_password_len); | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 86 | 	} | 
 | 87 | #endif /* CONFIG_WPS_NFC */ | 
 | 88 |  | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 89 | 	data->pbc = cfg->pbc; | 
 | 90 | 	if (cfg->pbc) { | 
 | 91 | 		/* Use special PIN '00000000' for PBC */ | 
 | 92 | 		data->dev_pw_id = DEV_PW_PUSHBUTTON; | 
| Dmitry Shmidt | c281702 | 2014-07-02 10:32:10 -0700 | [diff] [blame] | 93 | 		bin_clear_free(data->dev_password, data->dev_password_len); | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 94 | 		data->dev_password = (u8 *) os_strdup("00000000"); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 95 | 		if (data->dev_password == NULL) { | 
 | 96 | 			os_free(data); | 
 | 97 | 			return NULL; | 
 | 98 | 		} | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 99 | 		data->dev_password_len = 8; | 
 | 100 | 	} | 
 | 101 |  | 
 | 102 | 	data->state = data->registrar ? RECV_M1 : SEND_M1; | 
 | 103 |  | 
 | 104 | 	if (cfg->assoc_wps_ie) { | 
 | 105 | 		struct wps_parse_attr attr; | 
 | 106 | 		wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq", | 
 | 107 | 				cfg->assoc_wps_ie); | 
 | 108 | 		if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) { | 
 | 109 | 			wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE " | 
 | 110 | 				   "from (Re)AssocReq"); | 
 | 111 | 		} else if (attr.request_type == NULL) { | 
 | 112 | 			wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute " | 
 | 113 | 				   "in (Re)AssocReq WPS IE"); | 
 | 114 | 		} else { | 
 | 115 | 			wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE " | 
 | 116 | 				   "in (Re)AssocReq WPS IE): %d", | 
 | 117 | 				   *attr.request_type); | 
 | 118 | 			data->request_type = *attr.request_type; | 
 | 119 | 		} | 
 | 120 | 	} | 
 | 121 |  | 
 | 122 | 	if (cfg->new_ap_settings) { | 
 | 123 | 		data->new_ap_settings = | 
| Dmitry Shmidt | d2986c2 | 2017-10-23 14:22:09 -0700 | [diff] [blame] | 124 | 			os_memdup(cfg->new_ap_settings, | 
 | 125 | 				  sizeof(*data->new_ap_settings)); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 126 | 		if (data->new_ap_settings == NULL) { | 
| Dmitry Shmidt | c281702 | 2014-07-02 10:32:10 -0700 | [diff] [blame] | 127 | 			bin_clear_free(data->dev_password, | 
 | 128 | 				       data->dev_password_len); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 129 | 			os_free(data); | 
 | 130 | 			return NULL; | 
 | 131 | 		} | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 132 | 	} | 
 | 133 |  | 
 | 134 | 	if (cfg->peer_addr) | 
 | 135 | 		os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN); | 
 | 136 | 	if (cfg->p2p_dev_addr) | 
 | 137 | 		os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN); | 
 | 138 |  | 
 | 139 | 	data->use_psk_key = cfg->use_psk_key; | 
| Jouni Malinen | 87fd279 | 2011-05-16 18:35:42 +0300 | [diff] [blame] | 140 | 	data->pbc_in_m1 = cfg->pbc_in_m1; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 141 |  | 
| Dmitry Shmidt | cf32e60 | 2014-01-28 10:57:39 -0800 | [diff] [blame] | 142 | 	if (cfg->peer_pubkey_hash) { | 
 | 143 | 		os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash, | 
 | 144 | 			  WPS_OOB_PUBKEY_HASH_LEN); | 
 | 145 | 		data->peer_pubkey_hash_set = 1; | 
 | 146 | 	} | 
 | 147 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 148 | 	data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta; | 
| Sunil Ravi | 99c035e | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 149 | 	data->multi_ap_profile = cfg->multi_ap_profile; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 150 |  | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 151 | 	return data; | 
 | 152 | } | 
 | 153 |  | 
 | 154 |  | 
 | 155 | /** | 
 | 156 |  * wps_deinit - Deinitialize WPS Registration protocol data | 
 | 157 |  * @data: WPS Registration protocol data from wps_init() | 
 | 158 |  */ | 
 | 159 | void wps_deinit(struct wps_data *data) | 
 | 160 | { | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 161 | #ifdef CONFIG_WPS_NFC | 
 | 162 | 	if (data->registrar && data->nfc_pw_token) | 
 | 163 | 		wps_registrar_remove_nfc_pw_token(data->wps->registrar, | 
 | 164 | 						  data->nfc_pw_token); | 
 | 165 | #endif /* CONFIG_WPS_NFC */ | 
 | 166 |  | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 167 | 	if (data->wps_pin_revealed) { | 
 | 168 | 		wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and " | 
 | 169 | 			   "negotiation failed"); | 
 | 170 | 		if (data->registrar) | 
 | 171 | 			wps_registrar_invalidate_pin(data->wps->registrar, | 
 | 172 | 						     data->uuid_e); | 
 | 173 | 	} else if (data->registrar) | 
 | 174 | 		wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e); | 
 | 175 |  | 
| Dmitry Shmidt | 849734c | 2016-05-27 09:59:01 -0700 | [diff] [blame] | 176 | 	wpabuf_clear_free(data->dh_privkey); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 177 | 	wpabuf_free(data->dh_pubkey_e); | 
 | 178 | 	wpabuf_free(data->dh_pubkey_r); | 
 | 179 | 	wpabuf_free(data->last_msg); | 
| Dmitry Shmidt | c281702 | 2014-07-02 10:32:10 -0700 | [diff] [blame] | 180 | 	bin_clear_free(data->dev_password, data->dev_password_len); | 
 | 181 | 	bin_clear_free(data->alt_dev_password, data->alt_dev_password_len); | 
 | 182 | 	bin_clear_free(data->new_psk, data->new_psk_len); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 183 | 	wps_device_data_free(&data->peer_dev); | 
| Dmitry Shmidt | c281702 | 2014-07-02 10:32:10 -0700 | [diff] [blame] | 184 | 	bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings)); | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 185 | 	dh5_free(data->dh_ctx); | 
 | 186 | 	os_free(data); | 
 | 187 | } | 
 | 188 |  | 
 | 189 |  | 
 | 190 | /** | 
 | 191 |  * wps_process_msg - Process a WPS message | 
 | 192 |  * @wps: WPS Registration protocol data from wps_init() | 
 | 193 |  * @op_code: Message OP Code | 
 | 194 |  * @msg: Message data | 
 | 195 |  * Returns: Processing result | 
 | 196 |  * | 
 | 197 |  * This function is used to process WPS messages with OP Codes WSC_ACK, | 
 | 198 |  * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is | 
 | 199 |  * responsible for reassembling the messages before calling this function. | 
 | 200 |  * Response to this message is built by calling wps_get_msg(). | 
 | 201 |  */ | 
 | 202 | enum wps_process_res wps_process_msg(struct wps_data *wps, | 
 | 203 | 				     enum wsc_op_code op_code, | 
 | 204 | 				     const struct wpabuf *msg) | 
 | 205 | { | 
 | 206 | 	if (wps->registrar) | 
 | 207 | 		return wps_registrar_process_msg(wps, op_code, msg); | 
 | 208 | 	else | 
 | 209 | 		return wps_enrollee_process_msg(wps, op_code, msg); | 
 | 210 | } | 
 | 211 |  | 
 | 212 |  | 
 | 213 | /** | 
 | 214 |  * wps_get_msg - Build a WPS message | 
 | 215 |  * @wps: WPS Registration protocol data from wps_init() | 
 | 216 |  * @op_code: Buffer for returning message OP Code | 
 | 217 |  * Returns: The generated WPS message or %NULL on failure | 
 | 218 |  * | 
 | 219 |  * This function is used to build a response to a message processed by calling | 
 | 220 |  * wps_process_msg(). The caller is responsible for freeing the buffer. | 
 | 221 |  */ | 
 | 222 | struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code) | 
 | 223 | { | 
 | 224 | 	if (wps->registrar) | 
 | 225 | 		return wps_registrar_get_msg(wps, op_code); | 
 | 226 | 	else | 
 | 227 | 		return wps_enrollee_get_msg(wps, op_code); | 
 | 228 | } | 
 | 229 |  | 
 | 230 |  | 
 | 231 | /** | 
 | 232 |  * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC | 
 | 233 |  * @msg: WPS IE contents from Beacon or Probe Response frame | 
 | 234 |  * Returns: 1 if PBC Registrar is active, 0 if not | 
 | 235 |  */ | 
 | 236 | int wps_is_selected_pbc_registrar(const struct wpabuf *msg) | 
 | 237 | { | 
 | 238 | 	struct wps_parse_attr attr; | 
 | 239 |  | 
 | 240 | 	/* | 
 | 241 | 	 * In theory, this could also verify that attr.sel_reg_config_methods | 
 | 242 | 	 * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations | 
 | 243 | 	 * do not set Selected Registrar Config Methods attribute properly, so | 
 | 244 | 	 * it is safer to just use Device Password ID here. | 
 | 245 | 	 */ | 
 | 246 |  | 
 | 247 | 	if (wps_parse_msg(msg, &attr) < 0 || | 
 | 248 | 	    !attr.selected_registrar || *attr.selected_registrar == 0 || | 
 | 249 | 	    !attr.dev_password_id || | 
 | 250 | 	    WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON) | 
 | 251 | 		return 0; | 
 | 252 |  | 
 | 253 | #ifdef CONFIG_WPS_STRICT | 
 | 254 | 	if (!attr.sel_reg_config_methods || | 
 | 255 | 	    !(WPA_GET_BE16(attr.sel_reg_config_methods) & | 
 | 256 | 	      WPS_CONFIG_PUSHBUTTON)) | 
 | 257 | 		return 0; | 
 | 258 | #endif /* CONFIG_WPS_STRICT */ | 
 | 259 |  | 
 | 260 | 	return 1; | 
 | 261 | } | 
 | 262 |  | 
 | 263 |  | 
 | 264 | static int is_selected_pin_registrar(struct wps_parse_attr *attr) | 
 | 265 | { | 
 | 266 | 	/* | 
 | 267 | 	 * In theory, this could also verify that attr.sel_reg_config_methods | 
 | 268 | 	 * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD, | 
 | 269 | 	 * but some deployed AP implementations do not set Selected Registrar | 
 | 270 | 	 * Config Methods attribute properly, so it is safer to just use | 
 | 271 | 	 * Device Password ID here. | 
 | 272 | 	 */ | 
 | 273 |  | 
 | 274 | 	if (!attr->selected_registrar || *attr->selected_registrar == 0) | 
 | 275 | 		return 0; | 
 | 276 |  | 
 | 277 | 	if (attr->dev_password_id != NULL && | 
 | 278 | 	    WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) | 
 | 279 | 		return 0; | 
 | 280 |  | 
 | 281 | #ifdef CONFIG_WPS_STRICT | 
 | 282 | 	if (!attr->sel_reg_config_methods || | 
 | 283 | 	    !(WPA_GET_BE16(attr->sel_reg_config_methods) & | 
 | 284 | 	      (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD))) | 
 | 285 | 		return 0; | 
 | 286 | #endif /* CONFIG_WPS_STRICT */ | 
 | 287 |  | 
 | 288 | 	return 1; | 
 | 289 | } | 
 | 290 |  | 
 | 291 |  | 
 | 292 | /** | 
 | 293 |  * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN | 
 | 294 |  * @msg: WPS IE contents from Beacon or Probe Response frame | 
 | 295 |  * Returns: 1 if PIN Registrar is active, 0 if not | 
 | 296 |  */ | 
 | 297 | int wps_is_selected_pin_registrar(const struct wpabuf *msg) | 
 | 298 | { | 
 | 299 | 	struct wps_parse_attr attr; | 
 | 300 |  | 
 | 301 | 	if (wps_parse_msg(msg, &attr) < 0) | 
 | 302 | 		return 0; | 
 | 303 |  | 
 | 304 | 	return is_selected_pin_registrar(&attr); | 
 | 305 | } | 
 | 306 |  | 
 | 307 |  | 
 | 308 | /** | 
 | 309 |  * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address | 
 | 310 |  * @msg: WPS IE contents from Beacon or Probe Response frame | 
 | 311 |  * @addr: MAC address to search for | 
 | 312 |  * @ver1_compat: Whether to use version 1 compatibility mode | 
| Dmitry Shmidt | 61d9df3 | 2012-08-29 16:22:06 -0700 | [diff] [blame] | 313 |  * Returns: 2 if the specified address is explicit authorized, 1 if address is | 
 | 314 |  * authorized (broadcast), 0 if not | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 315 |  */ | 
 | 316 | int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, | 
 | 317 | 			   int ver1_compat) | 
 | 318 | { | 
 | 319 | 	struct wps_parse_attr attr; | 
 | 320 | 	unsigned int i; | 
 | 321 | 	const u8 *pos; | 
 | 322 | 	const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | 
 | 323 |  | 
 | 324 | 	if (wps_parse_msg(msg, &attr) < 0) | 
 | 325 | 		return 0; | 
 | 326 |  | 
 | 327 | 	if (!attr.version2 && ver1_compat) { | 
 | 328 | 		/* | 
 | 329 | 		 * Version 1.0 AP - AuthorizedMACs not used, so revert back to | 
 | 330 | 		 * old mechanism of using SelectedRegistrar. | 
 | 331 | 		 */ | 
 | 332 | 		return is_selected_pin_registrar(&attr); | 
 | 333 | 	} | 
 | 334 |  | 
 | 335 | 	if (!attr.authorized_macs) | 
 | 336 | 		return 0; | 
 | 337 |  | 
 | 338 | 	pos = attr.authorized_macs; | 
 | 339 | 	for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) { | 
| Sunil Ravi | b0ac25f | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 340 | 		if (ether_addr_equal(pos, addr)) | 
| Dmitry Shmidt | 61d9df3 | 2012-08-29 16:22:06 -0700 | [diff] [blame] | 341 | 			return 2; | 
| Sunil Ravi | b0ac25f | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 342 | 		if (ether_addr_equal(pos, bcast)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 343 | 			return 1; | 
 | 344 | 		pos += ETH_ALEN; | 
 | 345 | 	} | 
 | 346 |  | 
 | 347 | 	return 0; | 
 | 348 | } | 
 | 349 |  | 
 | 350 |  | 
 | 351 | /** | 
 | 352 |  * wps_ap_priority_compar - Prioritize WPS IE from two APs | 
 | 353 |  * @wps_a: WPS IE contents from Beacon or Probe Response frame | 
 | 354 |  * @wps_b: WPS IE contents from Beacon or Probe Response frame | 
 | 355 |  * Returns: 1 if wps_b is considered more likely selection for WPS | 
 | 356 |  * provisioning, -1 if wps_a is considered more like, or 0 if no preference | 
 | 357 |  */ | 
 | 358 | int wps_ap_priority_compar(const struct wpabuf *wps_a, | 
 | 359 | 			   const struct wpabuf *wps_b) | 
 | 360 | { | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 361 | 	struct wps_parse_attr attr; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 362 | 	int sel_a, sel_b; | 
 | 363 |  | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 364 | 	if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 365 | 		return 1; | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 366 | 	sel_a = attr.selected_registrar && *attr.selected_registrar != 0; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 367 |  | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 368 | 	if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0) | 
 | 369 | 		return -1; | 
 | 370 | 	sel_b = attr.selected_registrar && *attr.selected_registrar != 0; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 371 |  | 
 | 372 | 	if (sel_a && !sel_b) | 
 | 373 | 		return -1; | 
 | 374 | 	if (!sel_a && sel_b) | 
 | 375 | 		return 1; | 
 | 376 |  | 
 | 377 | 	return 0; | 
 | 378 | } | 
 | 379 |  | 
 | 380 |  | 
 | 381 | /** | 
 | 382 |  * wps_get_uuid_e - Get UUID-E from WPS IE | 
 | 383 |  * @msg: WPS IE contents from Beacon or Probe Response frame | 
 | 384 |  * Returns: Pointer to UUID-E or %NULL if not included | 
 | 385 |  * | 
 | 386 |  * The returned pointer is to the msg contents and it remains valid only as | 
 | 387 |  * long as the msg buffer is valid. | 
 | 388 |  */ | 
 | 389 | const u8 * wps_get_uuid_e(const struct wpabuf *msg) | 
 | 390 | { | 
 | 391 | 	struct wps_parse_attr attr; | 
 | 392 |  | 
 | 393 | 	if (wps_parse_msg(msg, &attr) < 0) | 
 | 394 | 		return NULL; | 
 | 395 | 	return attr.uuid_e; | 
 | 396 | } | 
 | 397 |  | 
 | 398 |  | 
 | 399 | /** | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 400 |  * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0 | 
 | 401 |  */ | 
 | 402 | int wps_is_20(const struct wpabuf *msg) | 
 | 403 | { | 
 | 404 | 	struct wps_parse_attr attr; | 
 | 405 |  | 
 | 406 | 	if (msg == NULL || wps_parse_msg(msg, &attr) < 0) | 
 | 407 | 		return 0; | 
 | 408 | 	return attr.version2 != NULL; | 
 | 409 | } | 
 | 410 |  | 
 | 411 |  | 
 | 412 | /** | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 413 |  * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request | 
 | 414 |  * @req_type: Value for Request Type attribute | 
 | 415 |  * Returns: WPS IE or %NULL on failure | 
 | 416 |  * | 
 | 417 |  * The caller is responsible for freeing the buffer. | 
 | 418 |  */ | 
 | 419 | struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) | 
 | 420 | { | 
 | 421 | 	struct wpabuf *ie; | 
 | 422 | 	u8 *len; | 
 | 423 |  | 
 | 424 | 	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association " | 
 | 425 | 		   "Request"); | 
 | 426 | 	ie = wpabuf_alloc(100); | 
 | 427 | 	if (ie == NULL) | 
 | 428 | 		return NULL; | 
 | 429 |  | 
 | 430 | 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); | 
 | 431 | 	len = wpabuf_put(ie, 1); | 
 | 432 | 	wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); | 
 | 433 |  | 
 | 434 | 	if (wps_build_version(ie) || | 
 | 435 | 	    wps_build_req_type(ie, req_type) || | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 436 | 	    wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 437 | 		wpabuf_free(ie); | 
 | 438 | 		return NULL; | 
 | 439 | 	} | 
 | 440 |  | 
 | 441 | 	*len = wpabuf_len(ie) - 2; | 
 | 442 |  | 
 | 443 | 	return ie; | 
 | 444 | } | 
 | 445 |  | 
 | 446 |  | 
 | 447 | /** | 
 | 448 |  * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response | 
 | 449 |  * Returns: WPS IE or %NULL on failure | 
 | 450 |  * | 
 | 451 |  * The caller is responsible for freeing the buffer. | 
 | 452 |  */ | 
 | 453 | struct wpabuf * wps_build_assoc_resp_ie(void) | 
 | 454 | { | 
 | 455 | 	struct wpabuf *ie; | 
 | 456 | 	u8 *len; | 
 | 457 |  | 
 | 458 | 	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association " | 
 | 459 | 		   "Response"); | 
 | 460 | 	ie = wpabuf_alloc(100); | 
 | 461 | 	if (ie == NULL) | 
 | 462 | 		return NULL; | 
 | 463 |  | 
 | 464 | 	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); | 
 | 465 | 	len = wpabuf_put(ie, 1); | 
 | 466 | 	wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); | 
 | 467 |  | 
 | 468 | 	if (wps_build_version(ie) || | 
 | 469 | 	    wps_build_resp_type(ie, WPS_RESP_AP) || | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 470 | 	    wps_build_wfa_ext(ie, 0, NULL, 0, 0)) { | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 471 | 		wpabuf_free(ie); | 
 | 472 | 		return NULL; | 
 | 473 | 	} | 
 | 474 |  | 
 | 475 | 	*len = wpabuf_len(ie) - 2; | 
 | 476 |  | 
 | 477 | 	return ie; | 
 | 478 | } | 
 | 479 |  | 
 | 480 |  | 
 | 481 | /** | 
 | 482 |  * wps_build_probe_req_ie - Build WPS IE for Probe Request | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 483 |  * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for | 
 | 484 |  * most other use cases) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 485 |  * @dev: Device attributes | 
 | 486 |  * @uuid: Own UUID | 
 | 487 |  * @req_type: Value for Request Type attribute | 
 | 488 |  * @num_req_dev_types: Number of requested device types | 
 | 489 |  * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or | 
 | 490 |  *	%NULL if none | 
 | 491 |  * Returns: WPS IE or %NULL on failure | 
 | 492 |  * | 
 | 493 |  * The caller is responsible for freeing the buffer. | 
 | 494 |  */ | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 495 | struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev, | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 496 | 				       const u8 *uuid, | 
 | 497 | 				       enum wps_request_type req_type, | 
 | 498 | 				       unsigned int num_req_dev_types, | 
 | 499 | 				       const u8 *req_dev_types) | 
 | 500 | { | 
 | 501 | 	struct wpabuf *ie; | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 502 |  | 
 | 503 | 	wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request"); | 
 | 504 |  | 
 | 505 | 	ie = wpabuf_alloc(500); | 
 | 506 | 	if (ie == NULL) | 
 | 507 | 		return NULL; | 
 | 508 |  | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 509 | 	if (wps_build_version(ie) || | 
 | 510 | 	    wps_build_req_type(ie, req_type) || | 
| Dmitry Shmidt | 1f69aa5 | 2012-01-24 16:10:04 -0800 | [diff] [blame] | 511 | 	    wps_build_config_methods(ie, dev->config_methods) || | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 512 | 	    wps_build_uuid_e(ie, uuid) || | 
 | 513 | 	    wps_build_primary_dev_type(dev, ie) || | 
| Dmitry Shmidt | b7b4d0e | 2013-08-26 12:09:05 -0700 | [diff] [blame] | 514 | 	    wps_build_rf_bands(dev, ie, 0) || | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 515 | 	    wps_build_assoc_state(NULL, ie) || | 
 | 516 | 	    wps_build_config_error(ie, WPS_CFG_NO_ERROR) || | 
| Dmitry Shmidt | 0494959 | 2012-07-19 12:16:46 -0700 | [diff] [blame] | 517 | 	    wps_build_dev_password_id(ie, pw_id) || | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 518 | 	    wps_build_manufacturer(dev, ie) || | 
 | 519 | 	    wps_build_model_name(dev, ie) || | 
 | 520 | 	    wps_build_model_number(dev, ie) || | 
 | 521 | 	    wps_build_dev_name(dev, ie) || | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 522 | 	    wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) || | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 523 | 	    wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types) | 
 | 524 | 	    || | 
 | 525 | 	    wps_build_secondary_dev_type(dev, ie) | 
 | 526 | 		) { | 
 | 527 | 		wpabuf_free(ie); | 
 | 528 | 		return NULL; | 
 | 529 | 	} | 
 | 530 |  | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 531 | 	return wps_ie_encapsulate(ie); | 
 | 532 | } | 
 | 533 |  | 
 | 534 |  | 
 | 535 | void wps_free_pending_msgs(struct upnp_pending_message *msgs) | 
 | 536 | { | 
 | 537 | 	struct upnp_pending_message *p, *prev; | 
 | 538 | 	p = msgs; | 
 | 539 | 	while (p) { | 
 | 540 | 		prev = p; | 
 | 541 | 		p = p->next; | 
 | 542 | 		wpabuf_free(prev->msg); | 
 | 543 | 		os_free(prev); | 
 | 544 | 	} | 
 | 545 | } | 
 | 546 |  | 
 | 547 |  | 
 | 548 | int wps_attr_text(struct wpabuf *data, char *buf, char *end) | 
 | 549 | { | 
 | 550 | 	struct wps_parse_attr attr; | 
 | 551 | 	char *pos = buf; | 
 | 552 | 	int ret; | 
 | 553 |  | 
 | 554 | 	if (wps_parse_msg(data, &attr) < 0) | 
 | 555 | 		return -1; | 
 | 556 |  | 
 | 557 | 	if (attr.wps_state) { | 
 | 558 | 		if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED) | 
 | 559 | 			ret = os_snprintf(pos, end - pos, | 
 | 560 | 					  "wps_state=unconfigured\n"); | 
 | 561 | 		else if (*attr.wps_state == WPS_STATE_CONFIGURED) | 
 | 562 | 			ret = os_snprintf(pos, end - pos, | 
 | 563 | 					  "wps_state=configured\n"); | 
 | 564 | 		else | 
 | 565 | 			ret = 0; | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 566 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 567 | 			return pos - buf; | 
 | 568 | 		pos += ret; | 
 | 569 | 	} | 
 | 570 |  | 
 | 571 | 	if (attr.ap_setup_locked && *attr.ap_setup_locked) { | 
 | 572 | 		ret = os_snprintf(pos, end - pos, | 
 | 573 | 				  "wps_ap_setup_locked=1\n"); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 574 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 575 | 			return pos - buf; | 
 | 576 | 		pos += ret; | 
 | 577 | 	} | 
 | 578 |  | 
 | 579 | 	if (attr.selected_registrar && *attr.selected_registrar) { | 
 | 580 | 		ret = os_snprintf(pos, end - pos, | 
 | 581 | 				  "wps_selected_registrar=1\n"); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 582 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 583 | 			return pos - buf; | 
 | 584 | 		pos += ret; | 
 | 585 | 	} | 
 | 586 |  | 
 | 587 | 	if (attr.dev_password_id) { | 
 | 588 | 		ret = os_snprintf(pos, end - pos, | 
 | 589 | 				  "wps_device_password_id=%u\n", | 
 | 590 | 				  WPA_GET_BE16(attr.dev_password_id)); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 591 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 592 | 			return pos - buf; | 
 | 593 | 		pos += ret; | 
 | 594 | 	} | 
 | 595 |  | 
 | 596 | 	if (attr.sel_reg_config_methods) { | 
 | 597 | 		ret = os_snprintf(pos, end - pos, | 
 | 598 | 				  "wps_selected_registrar_config_methods=" | 
 | 599 | 				  "0x%04x\n", | 
 | 600 | 				  WPA_GET_BE16(attr.sel_reg_config_methods)); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 601 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 602 | 			return pos - buf; | 
 | 603 | 		pos += ret; | 
 | 604 | 	} | 
 | 605 |  | 
 | 606 | 	if (attr.primary_dev_type) { | 
 | 607 | 		char devtype[WPS_DEV_TYPE_BUFSIZE]; | 
 | 608 | 		ret = os_snprintf(pos, end - pos, | 
 | 609 | 				  "wps_primary_device_type=%s\n", | 
 | 610 | 				  wps_dev_type_bin2str(attr.primary_dev_type, | 
 | 611 | 						       devtype, | 
 | 612 | 						       sizeof(devtype))); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 613 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 614 | 			return pos - buf; | 
 | 615 | 		pos += ret; | 
 | 616 | 	} | 
 | 617 |  | 
 | 618 | 	if (attr.dev_name) { | 
 | 619 | 		char *str = os_malloc(attr.dev_name_len + 1); | 
 | 620 | 		size_t i; | 
 | 621 | 		if (str == NULL) | 
 | 622 | 			return pos - buf; | 
 | 623 | 		for (i = 0; i < attr.dev_name_len; i++) { | 
| Dmitry Shmidt | 9d9e602 | 2015-04-23 10:34:55 -0700 | [diff] [blame] | 624 | 			if (attr.dev_name[i] == 0 || | 
 | 625 | 			    is_ctrl_char(attr.dev_name[i])) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 626 | 				str[i] = '_'; | 
 | 627 | 			else | 
 | 628 | 				str[i] = attr.dev_name[i]; | 
 | 629 | 		} | 
 | 630 | 		str[i] = '\0'; | 
 | 631 | 		ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str); | 
 | 632 | 		os_free(str); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 633 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 634 | 			return pos - buf; | 
 | 635 | 		pos += ret; | 
 | 636 | 	} | 
 | 637 |  | 
 | 638 | 	if (attr.config_methods) { | 
 | 639 | 		ret = os_snprintf(pos, end - pos, | 
 | 640 | 				  "wps_config_methods=0x%04x\n", | 
 | 641 | 				  WPA_GET_BE16(attr.config_methods)); | 
| Dmitry Shmidt | 6c0da2b | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 642 | 		if (os_snprintf_error(end - pos, ret)) | 
| Dmitry Shmidt | 8d520ff | 2011-05-09 14:06:53 -0700 | [diff] [blame] | 643 | 			return pos - buf; | 
 | 644 | 		pos += ret; | 
 | 645 | 	} | 
 | 646 |  | 
 | 647 | 	return pos - buf; | 
 | 648 | } | 
| Dmitry Shmidt | b7b4d0e | 2013-08-26 12:09:05 -0700 | [diff] [blame] | 649 |  | 
 | 650 |  | 
 | 651 | const char * wps_ei_str(enum wps_error_indication ei) | 
 | 652 | { | 
 | 653 | 	switch (ei) { | 
 | 654 | 	case WPS_EI_NO_ERROR: | 
 | 655 | 		return "No Error"; | 
 | 656 | 	case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED: | 
 | 657 | 		return "TKIP Only Prohibited"; | 
 | 658 | 	case WPS_EI_SECURITY_WEP_PROHIBITED: | 
 | 659 | 		return "WEP Prohibited"; | 
 | 660 | 	case WPS_EI_AUTH_FAILURE: | 
 | 661 | 		return "Authentication Failure"; | 
 | 662 | 	default: | 
 | 663 | 		return "Unknown"; | 
 | 664 | 	} | 
 | 665 | } |