1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import android.os.Parcelable; 20 import android.os.Parcel; 21 22 import java.util.BitSet; 23 24 import java.nio.ByteBuffer; 25 26 import java.util.Date; 27 28 /** 29 * Describes information about a detected access point. In addition 30 * to the attributes described here, the supplicant keeps track of 31 * {@code quality}, {@code noise}, and {@code maxbitrate} attributes, 32 * but does not currently report them to external clients. 33 */ 34 public class WifiParser { 35 36 public WifiParser() {} 37 38 39 /* 40 * {@hide} 41 */ 42 class IE { 43 int id; 44 byte data[]; 45 } 46 47 private static final int VENDOR_SPECIFIC_IE = 221; 48 private static final int IEEE_RSN_IE = 48; //IEEE 2012 8.4.2.27 49 50 51 private static final int WPA_IE_VENDOR_TYPE = 0x0050f201; //WFA WPA vendor IE OUI/type 52 53 /* 54 * parse beacon or probe response frame and build the capabilities 55 * {@hide} 56 * 57 * This function is called so as to build the capabilities string of the scan result, hence it is called 58 * by AutoJoin controller when handling scan results that are coming from WifiScanner. 59 * 60 * It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, but build the 61 * ScanResult.capabilities String in a way that mirror the values returned by wpa_supplicant. 62 * 63 * Once the capabilities string is build, the ScanResult can be used be the system as if it was coming from supplicant. 64 */ 65 /* @hide 66 * */ 67 static public String parse_akm(IE full_IE[], BitSet ieee_cap) { 68 boolean privacy = false; 69 boolean error = false; 70 if (ieee_cap == null) 71 return null; 72 73 if (full_IE == null) 74 return null; 75 76 privacy = ieee_cap.get(4); 77 78 String capabilities = ""; 79 boolean rsne_found = false; 80 boolean wpa_found = false; 81 82 for (IE ie : full_IE) { 83 String security = ""; 84 if (ie.id == IEEE_RSN_IE) { 85 rsne_found = true; 86 //parsing WPA2 capabilities 87 88 ByteBuffer buf = ByteBuffer.wrap(ie.data); 89 90 int total_len = ie.data.length; 91 int offset = 2; 92 93 //version 94 if ((total_len - offset) < 2) { 95 //not enough space for version field 96 security = ""; 97 error = true; 98 break; 99 } 100 int val = 0; 101 if (0x0100 != buf.getShort(offset)) { 102 //incorrect version 103 security = ""; 104 error = true; 105 break; 106 } 107 offset += 2; 108 109 110 //group cipher 111 if ((total_len - offset) < 4) { 112 security = ""; //parse error on group cipher suite 113 error = true; 114 break; 115 } 116 offset += 4; //skip the group cipher 117 118 security = "[WPA2"; //found the RSNE IE, hence start building the capability string 119 120 //pairwise cipher 121 if ((total_len - offset) < 2) { 122 security = ""; //parse error no pairwise cipher 123 error = true; 124 break; 125 } 126 val = buf.getShort(offset); 127 if ((total_len - offset) < (2 + val * 4)) { 128 security = ""; //parse error no pairwise cipher 129 error = true; 130 break; 131 } 132 offset += 2 + val * 4; //skip the pairwise ciphers 133 134 //AKM 135 if ((total_len - offset) < 2) { 136 security = ""; //parse error no AKM 137 error = true; 138 break; 139 } 140 val = buf.getShort(offset); 141 if ((total_len - offset) < (2 + val * 4)) { 142 security = ""; //parse error no pairwise cipher 143 error = true; 144 break; 145 } 146 offset += 2; 147 if (val == 0) { 148 security += "-EAP"; //default AKM 149 } 150 for (int i = 0; i < val; i++) { 151 int akm = buf.getInt(offset); 152 boolean found = false; 153 switch (akm) { 154 case 0x01ac0f00: 155 security += found ? "+" : "-" + "EAP"; 156 found = true; 157 break; 158 case 0x02ac0f00: 159 security += found ? "+" : "-" + "PSK"; //PSK as 802.11-2012 11.6.1.2 160 found = true; 161 break; 162 case 0x03ac0f00: 163 security += found ? "+" : "-" + "FT/EAP"; 164 found = true; 165 break; 166 case 0x04ac0f00: 167 security += found ? "+" : "-" + "FT/PSK"; 168 found = true; 169 break; 170 case 0x06ac0f00: 171 security += found ? "+" : "-" + "PSK-SHA256"; 172 found = true; 173 break; 174 case 0x05ac0f00: 175 security += found ? "+" : "-" + "EAP-SHA256"; 176 found = true; 177 break; 178 } 179 offset += 4; 180 } 181 //we parsed what we want at this point 182 security += "]"; 183 capabilities += security; 184 185 } 186 187 if (ie.id == VENDOR_SPECIFIC_IE) { 188 int total_len = ie.data.length; 189 int offset = 2; 190 191 //version 192 if ((total_len - offset) < 4) { 193 //not enough space for OUI and type field 194 security = ""; 195 error = true; 196 break; 197 } 198 199 ByteBuffer buf = ByteBuffer.wrap(ie.data); 200 201 if (buf.getInt(offset) != 0x01F25000) { 202 //look for HS2.0 and WPA IE 203 security = ""; 204 continue; 205 } 206 207 security = "[WPA"; //prep the string for WPA 208 209 //version 210 if ((total_len - offset) < 2) { 211 //not enough space for version field 212 security = ""; 213 error = true; 214 break; 215 } 216 int val = 0; 217 if (0x0100 != buf.getShort(offset)) { 218 //incorrect version 219 security = ""; 220 error = true; 221 break; 222 } 223 offset += 2; 224 225 226 //group cipher 227 if ((total_len - offset) < 4) { 228 security = ""; //parse error on group cipher suite 229 error = true; 230 break; 231 } 232 offset += 4; //skip the group cipher 233 234 235 //pairwise cipher 236 if ((total_len - offset) < 2) { 237 security = ""; //parse error no pairwise cipher 238 error = true; 239 break; 240 } 241 val = buf.getShort(offset); 242 if ((total_len - offset) < (2 + val * 4)) { 243 security = ""; //parse error no pairwise cipher 244 error = true; 245 break; 246 } 247 offset += 2 + val * 4; //skip the pairwise ciphers 248 249 //AKM 250 if ((total_len - offset) < 2) { 251 security = ""; //parse error no AKM 252 error = true; 253 break; 254 } 255 val = buf.getShort(offset); 256 if ((total_len - offset) < (2 + val * 4)) { 257 security = ""; //parse error no pairwise cipher 258 error = true; 259 break; 260 } 261 offset += 2; 262 if (val == 0) { 263 security += "-EAP"; //default AKM 264 } 265 for (int i = 0; i < val; i++) { 266 int akm = buf.getInt(offset); 267 boolean found = false; 268 switch (akm) { 269 case 0x01f25000: 270 security += found ? "+" : "-" + "EAP"; 271 found = true; 272 break; 273 case 0x02f25000: 274 security += found ? "+" : "-" + "PSK"; //PSK as 802.11-2012 11.6.1.2 275 found = true; 276 break; 277 278 } 279 offset += 4; 280 } 281 //we parsed what we want at this point 282 security += "]"; 283 } 284 } 285 286 if (rsne_found == false && wpa_found == false && privacy) { 287 //private Beacon without an RSNE or WPA IE, hence WEP0 288 capabilities += "[WEP]"; 289 } 290 291 if (error) 292 return null; 293 else 294 return capabilities; 295 } 296 297 298 } 299