1 /* 2 * Copyright (C) 2018 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.googlecode.android_scripting.facade.net; 18 19 import android.app.Service; 20 import android.content.Context; 21 import android.net.InetAddresses; 22 import android.net.IpSecAlgorithm; 23 import android.net.IpSecManager; 24 import android.net.IpSecManager.ResourceUnavailableException; 25 import android.net.IpSecManager.SecurityParameterIndex; 26 import android.net.IpSecManager.SpiUnavailableException; 27 import android.net.IpSecManager.UdpEncapsulationSocket; 28 import android.net.IpSecTransform; 29 import android.net.IpSecTransform.Builder; 30 31 import com.google.common.io.BaseEncoding; 32 import com.googlecode.android_scripting.Log; 33 import com.googlecode.android_scripting.facade.FacadeManager; 34 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 35 import com.googlecode.android_scripting.rpc.Rpc; 36 import com.googlecode.android_scripting.rpc.RpcOptional; 37 import com.googlecode.android_scripting.rpc.RpcParameter; 38 39 import java.io.FileDescriptor; 40 import java.io.IOException; 41 import java.net.DatagramSocket; 42 import java.net.InetAddress; 43 import java.net.Socket; 44 import java.util.HashMap; 45 46 /* 47 * Access IpSecManager functions. 48 */ 49 public class IpSecManagerFacade extends RpcReceiver { 50 51 private final IpSecManager mIpSecManager; 52 private final Service mService; 53 private final Context mContext; 54 private static HashMap<String, SecurityParameterIndex> sSpiHashMap = 55 new HashMap<String, SecurityParameterIndex>(); 56 private static HashMap<String, IpSecTransform> sTransformHashMap = 57 new HashMap<String, IpSecTransform>(); 58 private static HashMap<String, UdpEncapsulationSocket> sUdpEncapHashMap = 59 new HashMap<String, UdpEncapsulationSocket>(); 60 IpSecManagerFacade(FacadeManager manager)61 public IpSecManagerFacade(FacadeManager manager) { 62 super(manager); 63 mService = manager.getService(); 64 mContext = mService.getBaseContext(); 65 mIpSecManager = (IpSecManager) mService.getSystemService(Context.IPSEC_SERVICE); 66 } 67 createTransportModeTransform( String encAlgo, byte[] cryptKey, String authAlgo, byte[] authKey, Integer truncBits, SecurityParameterIndex spi, InetAddress addr, UdpEncapsulationSocket udpEncapSocket)68 private IpSecTransform createTransportModeTransform( 69 String encAlgo, 70 byte[] cryptKey, 71 String authAlgo, 72 byte[] authKey, 73 Integer truncBits, 74 SecurityParameterIndex spi, 75 InetAddress addr, 76 UdpEncapsulationSocket udpEncapSocket) { 77 Builder builder = new Builder(mContext); 78 builder = builder.setEncryption(new IpSecAlgorithm(encAlgo, cryptKey)); 79 builder = 80 builder.setAuthentication( 81 new IpSecAlgorithm(authAlgo, authKey, truncBits.intValue())); 82 if (udpEncapSocket != null) { 83 builder = builder.setIpv4Encapsulation(udpEncapSocket, udpEncapSocket.getPort()); 84 } 85 try { 86 return builder.buildTransportModeTransform(addr, spi); 87 } catch (SpiUnavailableException | IOException | ResourceUnavailableException e) { 88 Log.e("IpSec: Cannot create Transport mode transform" + e.toString()); 89 } 90 return null; 91 } 92 allocateSpi(InetAddress inetAddr)93 private SecurityParameterIndex allocateSpi(InetAddress inetAddr) { 94 try { 95 return mIpSecManager.allocateSecurityParameterIndex(inetAddr); 96 } catch (ResourceUnavailableException e) { 97 Log.e("IpSec: Reserve SPI failure " + e.toString()); 98 } 99 return null; 100 } 101 allocateSpi(InetAddress inetAddr, int requestedSpi)102 private SecurityParameterIndex allocateSpi(InetAddress inetAddr, int requestedSpi) { 103 try { 104 return mIpSecManager.allocateSecurityParameterIndex(inetAddr, requestedSpi); 105 } catch (SpiUnavailableException | ResourceUnavailableException e) { 106 Log.e("IpSec: Reserve SPI failure " + e.toString()); 107 } 108 return null; 109 } 110 openUdpEncapSocket()111 private UdpEncapsulationSocket openUdpEncapSocket() { 112 UdpEncapsulationSocket udpEncapSocket = null; 113 try { 114 return mIpSecManager.openUdpEncapsulationSocket(); 115 } catch (ResourceUnavailableException | IOException e) { 116 Log.e("IpSec: Failed to open udp encap socket " + e.toString()); 117 } 118 return null; 119 } 120 openUdpEncapSocket(int port)121 private UdpEncapsulationSocket openUdpEncapSocket(int port) { 122 try { 123 return mIpSecManager.openUdpEncapsulationSocket(port); 124 } catch (ResourceUnavailableException | IOException e) { 125 Log.e("IpSec: Failed to open udp encap socket " + e.toString()); 126 } 127 return null; 128 } 129 getSpiId(SecurityParameterIndex spi)130 private String getSpiId(SecurityParameterIndex spi) { 131 return "SPI:" + spi.hashCode(); 132 } 133 getTransformId(IpSecTransform transform)134 private String getTransformId(IpSecTransform transform) { 135 return "TRANSFORM:" + transform.hashCode(); 136 } 137 getUdpEncapSockId(UdpEncapsulationSocket socket)138 private String getUdpEncapSockId(UdpEncapsulationSocket socket) { 139 return "UDPENCAPSOCK:" + socket.hashCode(); 140 } 141 142 /** 143 * Method to retrieve UdpEncapsulationSocket from hash key 144 * @param id : Hash key in String 145 * @return UdpEncapsulationSocket object 146 */ getUdpEncapsulationSocket(String id)147 public static UdpEncapsulationSocket getUdpEncapsulationSocket(String id) { 148 return sUdpEncapHashMap.get(id); 149 } 150 151 /** 152 * Apply transport mode transform to FileDescriptor 153 * @param socketFd : Hash key of FileDescriptor object 154 * @param direction : In or Out direction to apply transform to 155 * @param id : Hash key of the transform 156 * @return True if transform is applied successfully 157 */ 158 @Rpc(description = "Apply transport mode transform to FileDescriptor", returns = "True/False") ipSecApplyTransportModeTransformFileDescriptor( String socketFd, Integer direction, String id)159 public Boolean ipSecApplyTransportModeTransformFileDescriptor( 160 String socketFd, 161 Integer direction, 162 String id) { 163 if (socketFd == null) { 164 Log.e("IpSec: Received null FileDescriptor key"); 165 return false; 166 } 167 FileDescriptor fd = SocketFacade.getFileDescriptor(socketFd); 168 IpSecTransform transform = sTransformHashMap.get(id); 169 if (transform == null) { 170 Log.e("IpSec: Transform does not exist for the requested id"); 171 return false; 172 } 173 try { 174 mIpSecManager.applyTransportModeTransform(fd, direction.intValue(), transform); 175 } catch (IOException e) { 176 Log.e("IpSec: Cannot apply transform to socket " + e.toString()); 177 return false; 178 } 179 return true; 180 } 181 182 /** 183 * Remove transport mode transform from a FileDescriptor 184 * @param socketFd : Hash key of FileDescriptor object 185 * @returns True if transform is removed successfully 186 */ 187 @Rpc(description = "Remove transport mode transform to FileDescriptor", returns = "True/False") ipSecRemoveTransportModeTransformsFileDescriptor(String socketFd)188 public Boolean ipSecRemoveTransportModeTransformsFileDescriptor(String socketFd) { 189 if (socketFd == null) { 190 Log.e("IpSec: Received null FileDescriptor key"); 191 return false; 192 } 193 FileDescriptor fd = SocketFacade.getFileDescriptor(socketFd); 194 try { 195 mIpSecManager.removeTransportModeTransforms(fd); 196 return true; 197 } catch (IOException e) { 198 Log.e("IpSec: Failed to remove transform " + e.toString()); 199 } 200 return false; 201 } 202 203 /** 204 * Apply transport mode transform to DatagramSocket 205 * @param socketId : Hash key of DatagramSocket 206 * @param direction : In or Out direction to apply transform to 207 * @param transformId : Hash key of Transform to apply 208 * @return True if transform is applied successfully 209 */ 210 @Rpc(description = "Apply transport mode transform to DatagramSocket", returns = "True/False") ipSecApplyTransportModeTransformDatagramSocket( String socketId, Integer direction, String transformId)211 public Boolean ipSecApplyTransportModeTransformDatagramSocket( 212 String socketId, 213 Integer direction, 214 String transformId) { 215 if (socketId == null) { 216 Log.e("IpSec: Received null DatagramSocket key"); 217 return false; 218 } 219 DatagramSocket socket = SocketFacade.getDatagramSocket(socketId); 220 IpSecTransform transform = sTransformHashMap.get(transformId); 221 if (transform == null) { 222 Log.e("IpSec: Transform does not exist for the requested id"); 223 return false; 224 } 225 try { 226 mIpSecManager.applyTransportModeTransform(socket, direction.intValue(), transform); 227 } catch (IOException e) { 228 Log.e("IpSec: Cannot apply transform to socket " + e.toString()); 229 return false; 230 } 231 return true; 232 } 233 234 /** 235 * Remove transport mode transform from DatagramSocket 236 * @param socketId : Hash key of DatagramSocket 237 * @return True if removing transform is successful 238 */ 239 @Rpc(description = "Remove transport mode tranform from DatagramSocket", returns = "True/False") ipSecRemoveTransportModeTransformsDatagramSocket(String socketId)240 public Boolean ipSecRemoveTransportModeTransformsDatagramSocket(String socketId) { 241 if (socketId == null) { 242 Log.e("IpSec: Received null DatagramSocket key"); 243 return false; 244 } 245 DatagramSocket socket = SocketFacade.getDatagramSocket(socketId); 246 try { 247 mIpSecManager.removeTransportModeTransforms(socket); 248 return true; 249 } catch (IOException e) { 250 Log.e("IpSec: Failed to remove transform " + e.toString()); 251 } 252 return false; 253 } 254 255 /** 256 * Apply transport mode transform to DatagramSocket 257 * @param socketId : Hash key of Socket 258 * @param direction : In or Out direction to apply transform to 259 * @param transformId : Hash key of Transform to apply 260 * @return True if transform is applied successfully 261 */ 262 @Rpc(description = "Apply transport mode transform to Socket", returns = "True/False") ipSecApplyTransportModeTransformSocket( String socketId, Integer direction, String transformId)263 public Boolean ipSecApplyTransportModeTransformSocket( 264 String socketId, 265 Integer direction, 266 String transformId) { 267 if (socketId == null) { 268 Log.e("IpSec: Received null Socket key"); 269 return false; 270 } 271 Socket socket = SocketFacade.getSocket(socketId); 272 IpSecTransform transform = sTransformHashMap.get(transformId); 273 if (transform == null) { 274 Log.e("IpSec: Transform does not exist for the requested id"); 275 return false; 276 } 277 try { 278 mIpSecManager.applyTransportModeTransform(socket, direction.intValue(), transform); 279 } catch (IOException e) { 280 Log.e("IpSec: Cannot apply transform to socket " + e.toString()); 281 return false; 282 } 283 return true; 284 } 285 286 /** 287 * Remove transport mode transform from Socket 288 * @param socketId : Hash key of DatagramSocket 289 * @return True if removing transform is successful 290 */ 291 @Rpc(description = "Remove transport mode tranform from Socket", returns = "True/False") ipSecRemoveTransportModeTransformsSocket(String socketId)292 public Boolean ipSecRemoveTransportModeTransformsSocket(String socketId) { 293 if (socketId == null) { 294 Log.e("IpSec: Received null Socket key"); 295 return false; 296 } 297 Socket socket = SocketFacade.getSocket(socketId); 298 try { 299 mIpSecManager.removeTransportModeTransforms(socket); 300 return true; 301 } catch (IOException e) { 302 Log.e("IpSec: Failed to remove transform " + e.toString()); 303 } 304 return false; 305 } 306 307 @Rpc(description = "Create a transform mode transform", returns = "Hash of transform object") ipSecCreateTransportModeTransform( String encAlgo, String cryptKeyHex, String authAlgo, String authKeyHex, Integer truncBits, String spiId, String addr, String udpEncapSockId)308 public String ipSecCreateTransportModeTransform( 309 String encAlgo, 310 String cryptKeyHex, 311 String authAlgo, 312 String authKeyHex, 313 Integer truncBits, 314 String spiId, 315 String addr, 316 String udpEncapSockId) { 317 IpSecTransform transform = null; 318 InetAddress inetAddr = InetAddresses.parseNumericAddress(addr); 319 UdpEncapsulationSocket udpEncapSocket = sUdpEncapHashMap.get(udpEncapSockId); 320 SecurityParameterIndex spi = sSpiHashMap.get(spiId); 321 if (spi == null) { 322 Log.e("IpSec: SPI does not exist for the requested spiId"); 323 return null; 324 } 325 byte[] cryptKey = BaseEncoding.base16().decode(cryptKeyHex.toUpperCase()); 326 byte[] authKey = BaseEncoding.base16().decode(authKeyHex.toUpperCase()); 327 transform = createTransportModeTransform(encAlgo, cryptKey, authAlgo, authKey, truncBits, 328 spi, inetAddr, udpEncapSocket); 329 if (transform == null) return null; 330 String id = getTransformId(transform); 331 sTransformHashMap.put(id, transform); 332 return id; 333 } 334 335 @Rpc(description = "Get transform status", returns = "True if transform exists") ipSecGetTransformStatus(String id)336 public Boolean ipSecGetTransformStatus(String id) { 337 IpSecTransform transform = sTransformHashMap.get(id); 338 if (transform == null) { 339 Log.e("IpSec: Transform does not exist for the requested id"); 340 return false; 341 } 342 return true; 343 } 344 345 @Rpc(description = "Destroy transport mode transform") ipSecDestroyTransportModeTransform(String id)346 public void ipSecDestroyTransportModeTransform(String id) { 347 IpSecTransform transform = sTransformHashMap.get(id); 348 if (transform == null) { 349 Log.e("IpSec: Transform does not exist for the requested id"); 350 return; 351 } 352 transform.close(); 353 sTransformHashMap.remove(id); 354 } 355 356 @Rpc(description = "Open UDP encap socket", returns = "Hash of UDP encap socket object") ipSecOpenUdpEncapsulationSocket( @pcParametername = "port") @pcOptional Integer port)357 public String ipSecOpenUdpEncapsulationSocket( 358 @RpcParameter(name = "port") @RpcOptional Integer port) { 359 UdpEncapsulationSocket udpEncapSocket = null; 360 if (port == null) { 361 udpEncapSocket = openUdpEncapSocket(); 362 } else { 363 udpEncapSocket = openUdpEncapSocket(port.intValue()); 364 } 365 if (udpEncapSocket == null) return null; 366 String id = getUdpEncapSockId(udpEncapSocket); 367 sUdpEncapHashMap.put(id, udpEncapSocket); 368 return id; 369 } 370 371 @Rpc(description = "Close UDP encapsulation socket", returns = "True if socket is closed") ipSecCloseUdpEncapsulationSocket(String id)372 public Boolean ipSecCloseUdpEncapsulationSocket(String id) { 373 try { 374 UdpEncapsulationSocket udpEncapSocket = sUdpEncapHashMap.get(id); 375 udpEncapSocket.close(); 376 sUdpEncapHashMap.remove(id); 377 return true; 378 } catch (IOException e) { 379 Log.e("IpSec: Failed to close udp encap socket " + e.toString()); 380 } 381 return false; 382 } 383 384 @Rpc(description = "Allocate a Security Parameter Index", returns = "Hash of SPI object") ipSecAllocateSecurityParameterIndex( @pcParametername = "addr") String addr, @RpcParameter(name = "requestedSpi") @RpcOptional Integer requestedSpi)385 public String ipSecAllocateSecurityParameterIndex( 386 @RpcParameter(name = "addr") String addr, 387 @RpcParameter(name = "requestedSpi") @RpcOptional Integer requestedSpi) { 388 InetAddress inetAddr = InetAddresses.parseNumericAddress(addr); 389 SecurityParameterIndex spi = null; 390 if (requestedSpi == null) { 391 spi = allocateSpi(inetAddr); 392 } else { 393 spi = allocateSpi(inetAddr, requestedSpi.intValue()); 394 } 395 if (spi == null) return null; 396 String id = getSpiId(spi); 397 sSpiHashMap.put(id, spi); 398 return id; 399 } 400 401 @Rpc(description = "Get Security Parameter Index", returns = "Returns SPI value") ipSecGetSecurityParameterIndex(String id)402 public Integer ipSecGetSecurityParameterIndex(String id) { 403 SecurityParameterIndex spi = sSpiHashMap.get(id); 404 if (spi == null) { 405 Log.d("IpSec: SPI does not exist for the requested id"); 406 return 0; 407 } 408 return spi.getSpi(); 409 } 410 411 @Rpc(description = "Release a Security Parameter Index") ipSecReleaseSecurityParameterIndex(String id)412 public void ipSecReleaseSecurityParameterIndex(String id) { 413 SecurityParameterIndex spi = sSpiHashMap.get(id); 414 if (spi == null) { 415 Log.d("IpSec: SPI does not exist for the requested id"); 416 return; 417 } 418 spi.close(); 419 sSpiHashMap.remove(id); 420 } 421 422 @Override shutdown()423 public void shutdown() {} 424 } 425