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