1 /* 2 * Copyright (C) 2019 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 android.net.ipsec.ike; 18 19 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_APPLICATION_VERSION; 20 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP4_PCSCF; 21 import static com.android.internal.net.ipsec.ike.message.IkeConfigPayload.CONFIG_ATTR_IP6_PCSCF; 22 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.SuppressLint; 27 import android.annotation.SystemApi; 28 import android.net.eap.EapInfo; 29 30 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload; 31 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttribute; 32 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeAppVersion; 33 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv4Pcscf; 34 import com.android.internal.net.ipsec.ike.message.IkeConfigPayload.ConfigAttributeIpv6Pcscf; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.net.InetAddress; 39 import java.util.ArrayList; 40 import java.util.Collections; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Objects; 44 import java.util.Set; 45 46 /** 47 * IkeSessionConfiguration represents the negotiated configuration for a {@link IkeSession}. 48 * 49 * <p>Configurations include remote application version and enabled IKE extensions. 50 */ 51 public final class IkeSessionConfiguration { 52 /** @hide */ 53 @Retention(RetentionPolicy.SOURCE) 54 @IntDef({EXTENSION_TYPE_FRAGMENTATION, EXTENSION_TYPE_MOBIKE}) 55 public @interface ExtensionType {} 56 57 /** IKE Message Fragmentation */ 58 public static final int EXTENSION_TYPE_FRAGMENTATION = 1; 59 /** IKEv2 Mobility and Multihoming Protocol */ 60 public static final int EXTENSION_TYPE_MOBIKE = 2; 61 62 private static final int VALID_EXTENSION_MIN = EXTENSION_TYPE_FRAGMENTATION; 63 private static final int VALID_EXTENSION_MAX = EXTENSION_TYPE_MOBIKE; 64 65 private final String mRemoteApplicationVersion; 66 private final IkeSessionConnectionInfo mIkeConnInfo; 67 private final List<InetAddress> mPcscfServers = new ArrayList<>(); 68 private final List<byte[]> mRemoteVendorIds = new ArrayList<>(); 69 private final Set<Integer> mEnabledExtensions = new HashSet<>(); 70 private final EapInfo mEapInfo; 71 72 /** 73 * Construct an instance of {@link IkeSessionConfiguration}. 74 * 75 * <p>IkeSessionConfigurations may contain negotiated configuration information that is included 76 * in a Configure(Reply) Payload. Thus the input configPayload should always be a 77 * Configure(Reply), and never be a Configure(Request). 78 * 79 * @hide 80 */ IkeSessionConfiguration( IkeSessionConnectionInfo ikeConnInfo, IkeConfigPayload configPayload, List<byte[]> remoteVendorIds, List<Integer> enabledExtensions, EapInfo eapInfo)81 public IkeSessionConfiguration( 82 IkeSessionConnectionInfo ikeConnInfo, 83 IkeConfigPayload configPayload, 84 List<byte[]> remoteVendorIds, 85 List<Integer> enabledExtensions, 86 EapInfo eapInfo) { 87 mIkeConnInfo = ikeConnInfo; 88 mRemoteVendorIds.addAll(remoteVendorIds); 89 mEnabledExtensions.addAll(enabledExtensions); 90 mEapInfo = eapInfo; 91 92 String appVersion = ""; 93 if (configPayload != null) { 94 if (configPayload.configType != IkeConfigPayload.CONFIG_TYPE_REPLY) { 95 throw new IllegalArgumentException( 96 "Cannot build IkeSessionConfiguration with configuration type: " 97 + configPayload.configType); 98 } 99 100 for (ConfigAttribute attr : configPayload.recognizedAttributeList) { 101 if (attr.isEmptyValue()) continue; 102 switch (attr.attributeType) { 103 case CONFIG_ATTR_APPLICATION_VERSION: 104 ConfigAttributeAppVersion appVersionAttr = (ConfigAttributeAppVersion) attr; 105 appVersion = appVersionAttr.applicationVersion; 106 break; 107 case CONFIG_ATTR_IP4_PCSCF: 108 ConfigAttributeIpv4Pcscf ip4Pcscf = (ConfigAttributeIpv4Pcscf) attr; 109 mPcscfServers.add(ip4Pcscf.getAddress()); 110 break; 111 case CONFIG_ATTR_IP6_PCSCF: 112 ConfigAttributeIpv6Pcscf ip6Pcscf = (ConfigAttributeIpv6Pcscf) attr; 113 mPcscfServers.add(ip6Pcscf.getAddress()); 114 break; 115 default: 116 // Not relevant to IKE session 117 } 118 } 119 } 120 mRemoteApplicationVersion = appVersion; 121 validateOrThrow(); 122 } 123 124 /** 125 * Construct an instance of {@link IkeSessionConfiguration}. 126 * 127 * @hide 128 */ IkeSessionConfiguration( IkeSessionConnectionInfo ikeConnInfo, List<InetAddress> pcscfServers, List<byte[]> remoteVendorIds, Set<Integer> enabledExtensions, String remoteApplicationVersion, EapInfo eapInfo)129 private IkeSessionConfiguration( 130 IkeSessionConnectionInfo ikeConnInfo, 131 List<InetAddress> pcscfServers, 132 List<byte[]> remoteVendorIds, 133 Set<Integer> enabledExtensions, 134 String remoteApplicationVersion, 135 EapInfo eapInfo) { 136 mIkeConnInfo = ikeConnInfo; 137 mPcscfServers.addAll(pcscfServers); 138 mRemoteVendorIds.addAll(remoteVendorIds); 139 mEnabledExtensions.addAll(enabledExtensions); 140 mRemoteApplicationVersion = remoteApplicationVersion; 141 mEapInfo = eapInfo; 142 143 validateOrThrow(); 144 } 145 validateOrThrow()146 private void validateOrThrow() { 147 String errMsg = " was null"; 148 Objects.requireNonNull(mIkeConnInfo, "ikeConnInfo" + errMsg); 149 Objects.requireNonNull(mPcscfServers, "pcscfServers" + errMsg); 150 Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg); 151 Objects.requireNonNull(mRemoteApplicationVersion, "remoteApplicationVersion" + errMsg); 152 Objects.requireNonNull(mRemoteVendorIds, "remoteVendorIds" + errMsg); 153 } 154 155 /** 156 * Gets remote (server) version information. 157 * 158 * @return application version of the remote server, or an empty string if the remote server did 159 * not provide the application version. 160 */ 161 @NonNull getRemoteApplicationVersion()162 public String getRemoteApplicationVersion() { 163 return mRemoteApplicationVersion; 164 } 165 166 /** 167 * Returns remote vendor IDs received during IKE Session setup. 168 * 169 * <p>According to the IKEv2 specification (RFC 7296), a vendor ID may indicate the sender is 170 * capable of accepting certain extensions to the protocol, or it may simply identify the 171 * implementation as an aid in debugging. 172 * 173 * @return the vendor IDs of the remote server, or an empty list if no vendor ID is received 174 * during IKE Session setup. 175 */ 176 @NonNull getRemoteVendorIds()177 public List<byte[]> getRemoteVendorIds() { 178 return Collections.unmodifiableList(mRemoteVendorIds); 179 } 180 181 /** 182 * Checks if an IKE extension is enabled. 183 * 184 * <p>An IKE extension is enabled when both sides can support it. This negotiation always 185 * happens in IKE initial exchanges (IKE INIT and IKE AUTH). 186 * 187 * @param extensionType the extension type. 188 * @return {@code true} if this extension is enabled. 189 */ isIkeExtensionEnabled(@xtensionType int extensionType)190 public boolean isIkeExtensionEnabled(@ExtensionType int extensionType) { 191 return mEnabledExtensions.contains(extensionType); 192 } 193 194 /** 195 * Returns the assigned P_CSCF servers. 196 * 197 * @return the assigned P_CSCF servers, or an empty list when no servers are assigned by the 198 * remote IKE server. 199 * @hide 200 */ 201 @SystemApi 202 @NonNull getPcscfServers()203 public List<InetAddress> getPcscfServers() { 204 return Collections.unmodifiableList(mPcscfServers); 205 } 206 207 /** 208 * Returns the connection information. 209 * 210 * @return the IKE Session connection information. 211 */ 212 @NonNull getIkeSessionConnectionInfo()213 public IkeSessionConnectionInfo getIkeSessionConnectionInfo() { 214 return mIkeConnInfo; 215 } 216 217 /** 218 * Retrieves the EAP information. 219 * 220 * @return the EAP information provided by the server during EAP authentication (e.g. next 221 * re-authentication ID), or null if the server did not provide any information that will be 222 * useful after the authentication. 223 */ 224 @Nullable getEapInfo()225 public EapInfo getEapInfo() { 226 return mEapInfo; 227 } 228 229 /** 230 * This class can be used to incrementally construct a {@link IkeSessionConfiguration}. 231 * 232 * <p>Except for testing, IKE library users normally do not instantiate {@link 233 * IkeSessionConfiguration} themselves but instead get a reference via {@link 234 * IkeSessionCallback} 235 */ 236 public static final class Builder { 237 private final IkeSessionConnectionInfo mIkeConnInfo; 238 private final List<InetAddress> mPcscfServers = new ArrayList<>(); 239 private final List<byte[]> mRemoteVendorIds = new ArrayList<>(); 240 private final Set<Integer> mEnabledExtensions = new HashSet<>(); 241 private String mRemoteApplicationVersion = ""; 242 private EapInfo mEapInfo; 243 244 /** 245 * Constructs a Builder. 246 * 247 * @param ikeConnInfo the connection information 248 */ Builder(@onNull IkeSessionConnectionInfo ikeConnInfo)249 public Builder(@NonNull IkeSessionConnectionInfo ikeConnInfo) { 250 Objects.requireNonNull(ikeConnInfo, "ikeConnInfo was null"); 251 mIkeConnInfo = ikeConnInfo; 252 } 253 254 /** 255 * Adds an assigned P_CSCF server for the {@link IkeSessionConfiguration} being built. 256 * 257 * @param pcscfServer an assigned P_CSCF server 258 * @return Builder this, to facilitate chaining 259 * @hide 260 */ 261 @SystemApi 262 @NonNull addPcscfServer(@onNull InetAddress pcscfServer)263 public Builder addPcscfServer(@NonNull InetAddress pcscfServer) { 264 Objects.requireNonNull(pcscfServer, "pcscfServer was null"); 265 mPcscfServers.add(pcscfServer); 266 return this; 267 } 268 269 /** 270 * Clear all P_CSCF servers from the {@link IkeSessionConfiguration} being built. 271 * 272 * @return Builder this, to facilitate chaining 273 * @hide 274 */ 275 @SystemApi 276 @NonNull clearPcscfServers()277 public Builder clearPcscfServers() { 278 mPcscfServers.clear(); 279 return this; 280 } 281 282 /** 283 * Adds a remote vendor ID for the {@link IkeSessionConfiguration} being built. 284 * 285 * @param remoteVendorId a remote vendor ID 286 * @return Builder this, to facilitate chaining 287 */ 288 @NonNull addRemoteVendorId(@onNull byte[] remoteVendorId)289 public Builder addRemoteVendorId(@NonNull byte[] remoteVendorId) { 290 Objects.requireNonNull(remoteVendorId, "remoteVendorId was null"); 291 mRemoteVendorIds.add(remoteVendorId); 292 return this; 293 } 294 295 /** 296 * Clears all remote vendor IDs from the {@link IkeSessionConfiguration} being built. 297 * 298 * @return Builder this, to facilitate chaining 299 */ 300 @NonNull clearRemoteVendorIds()301 public Builder clearRemoteVendorIds() { 302 mRemoteVendorIds.clear(); 303 return this; 304 } 305 306 /** 307 * Sets the remote application version for the {@link IkeSessionConfiguration} being built. 308 * 309 * @param remoteApplicationVersion the remote application version. Defaults to an empty 310 * string. 311 * @return Builder this, to facilitate chaining 312 */ 313 @NonNull setRemoteApplicationVersion(@onNull String remoteApplicationVersion)314 public Builder setRemoteApplicationVersion(@NonNull String remoteApplicationVersion) { 315 Objects.requireNonNull(remoteApplicationVersion, "remoteApplicationVersion was null"); 316 mRemoteApplicationVersion = remoteApplicationVersion; 317 return this; 318 } 319 320 /** 321 * Clears the remote application version from the {@link IkeSessionConfiguration} being 322 * built. 323 * 324 * @return Builder this, to facilitate chaining 325 */ 326 @NonNull clearRemoteApplicationVersion()327 public Builder clearRemoteApplicationVersion() { 328 mRemoteApplicationVersion = ""; 329 return this; 330 } 331 validateExtensionOrThrow(@xtensionType int extensionType)332 private static void validateExtensionOrThrow(@ExtensionType int extensionType) { 333 if (extensionType >= VALID_EXTENSION_MIN && extensionType <= VALID_EXTENSION_MAX) { 334 return; 335 } 336 throw new IllegalArgumentException("Invalid extension type: " + extensionType); 337 } 338 339 /** 340 * Marks an IKE extension as enabled for the {@link IkeSessionConfiguration} being built. 341 * 342 * @param extensionType the enabled extension 343 * @return Builder this, to facilitate chaining 344 */ 345 // MissingGetterMatchingBuilder: Use #isIkeExtensionEnabled instead of #getIkeExtension 346 // because #isIkeExtensionEnabled allows callers to check the presence of an IKE extension 347 // more easily 348 @SuppressLint("MissingGetterMatchingBuilder") 349 @NonNull addIkeExtension(@xtensionType int extensionType)350 public Builder addIkeExtension(@ExtensionType int extensionType) { 351 validateExtensionOrThrow(extensionType); 352 mEnabledExtensions.add(extensionType); 353 return this; 354 } 355 356 /** 357 * Clear all enabled IKE extensions from the {@link IkeSessionConfiguration} being built. 358 * 359 * @return Builder this, to facilitate chaining 360 */ 361 @NonNull clearIkeExtensions()362 public Builder clearIkeExtensions() { 363 mEnabledExtensions.clear(); 364 return this; 365 } 366 367 /** 368 * Sets EapInfo for the {@link IkeSessionConfiguration} being built. 369 * 370 * @return Builder this, to facilitate chaining 371 */ 372 @NonNull setEapInfo(@ullable EapInfo eapInfo)373 public Builder setEapInfo(@Nullable EapInfo eapInfo) { 374 mEapInfo = eapInfo; 375 return this; 376 } 377 378 /** Constructs an {@link IkeSessionConfiguration} instance. */ 379 @NonNull build()380 public IkeSessionConfiguration build() { 381 return new IkeSessionConfiguration( 382 mIkeConnInfo, 383 mPcscfServers, 384 mRemoteVendorIds, 385 mEnabledExtensions, 386 mRemoteApplicationVersion, 387 mEapInfo); 388 } 389 } 390 } 391