1 /* 2 * Copyright (C) 2017 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; 18 19 import static android.Manifest.permission.DUMP; 20 import static android.net.IpSecManager.INVALID_RESOURCE_ID; 21 import static android.system.OsConstants.AF_INET; 22 import static android.system.OsConstants.EINVAL; 23 import static android.system.OsConstants.IPPROTO_UDP; 24 import static android.system.OsConstants.SOCK_DGRAM; 25 import static com.android.internal.util.Preconditions.checkNotNull; 26 27 import android.annotation.NonNull; 28 import android.app.AppOpsManager; 29 import android.content.Context; 30 import android.net.ConnectivityManager; 31 import android.net.IIpSecService; 32 import android.net.INetd; 33 import android.net.IpSecAlgorithm; 34 import android.net.IpSecConfig; 35 import android.net.IpSecManager; 36 import android.net.IpSecSpiResponse; 37 import android.net.IpSecTransform; 38 import android.net.IpSecTransformResponse; 39 import android.net.IpSecTunnelInterfaceResponse; 40 import android.net.IpSecUdpEncapResponse; 41 import android.net.LinkAddress; 42 import android.net.Network; 43 import android.net.NetworkUtils; 44 import android.net.TrafficStats; 45 import android.net.util.NetdService; 46 import android.os.Binder; 47 import android.os.DeadSystemException; 48 import android.os.IBinder; 49 import android.os.ParcelFileDescriptor; 50 import android.os.RemoteException; 51 import android.os.ServiceSpecificException; 52 import android.system.ErrnoException; 53 import android.system.Os; 54 import android.system.OsConstants; 55 import android.text.TextUtils; 56 import android.util.Log; 57 import android.util.Slog; 58 import android.util.SparseArray; 59 import android.util.SparseBooleanArray; 60 61 import com.android.internal.annotations.GuardedBy; 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.internal.util.Preconditions; 64 65 import java.io.FileDescriptor; 66 import java.io.IOException; 67 import java.io.PrintWriter; 68 import java.net.InetAddress; 69 import java.net.InetSocketAddress; 70 import java.net.UnknownHostException; 71 import java.util.ArrayList; 72 import java.util.List; 73 74 import libcore.io.IoUtils; 75 76 /** 77 * A service to manage multiple clients that want to access the IpSec API. The service is 78 * responsible for maintaining a list of clients and managing the resources (and related quotas) 79 * that each of them own. 80 * 81 * <p>Synchronization in IpSecService is done on all entrypoints due to potential race conditions at 82 * the kernel/xfrm level. Further, this allows the simplifying assumption to be made that only one 83 * thread is ever running at a time. 84 * 85 * @hide 86 */ 87 public class IpSecService extends IIpSecService.Stub { 88 private static final String TAG = "IpSecService"; 89 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 90 91 private static final String NETD_SERVICE_NAME = "netd"; 92 private static final int[] DIRECTIONS = 93 new int[] {IpSecManager.DIRECTION_OUT, IpSecManager.DIRECTION_IN}; 94 private static final String[] WILDCARD_ADDRESSES = new String[]{"0.0.0.0", "::"}; 95 96 private static final int NETD_FETCH_TIMEOUT_MS = 5000; // ms 97 private static final int MAX_PORT_BIND_ATTEMPTS = 10; 98 private static final InetAddress INADDR_ANY; 99 100 static { 101 try { 102 INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); 103 } catch (UnknownHostException e) { 104 throw new RuntimeException(e); 105 } 106 } 107 108 static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved 109 static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer 110 111 /* Binder context for this service */ 112 private final Context mContext; 113 114 /** 115 * The next non-repeating global ID for tracking resources between users, this service, and 116 * kernel data structures. Accessing this variable is not thread safe, so it is only read or 117 * modified within blocks synchronized on IpSecService.this. We want to avoid -1 118 * (INVALID_RESOURCE_ID) and 0 (we probably forgot to initialize it). 119 */ 120 @GuardedBy("IpSecService.this") 121 private int mNextResourceId = 1; 122 123 interface IpSecServiceConfiguration { getNetdInstance()124 INetd getNetdInstance() throws RemoteException; 125 126 static IpSecServiceConfiguration GETSRVINSTANCE = 127 new IpSecServiceConfiguration() { 128 @Override 129 public INetd getNetdInstance() throws RemoteException { 130 final INetd netd = NetdService.getInstance(); 131 if (netd == null) { 132 throw new RemoteException("Failed to Get Netd Instance"); 133 } 134 return netd; 135 } 136 }; 137 } 138 139 private final IpSecServiceConfiguration mSrvConfig; 140 final UidFdTagger mUidFdTagger; 141 142 /** 143 * Interface for user-reference and kernel-resource cleanup. 144 * 145 * <p>This interface must be implemented for a resource to be reference counted. 146 */ 147 @VisibleForTesting 148 public interface IResource { 149 /** 150 * Invalidates a IResource object, ensuring it is invalid for the purposes of allocating new 151 * objects dependent on it. 152 * 153 * <p>Implementations of this method are expected to remove references to the IResource 154 * object from the IpSecService's tracking arrays. The removal from the arrays ensures that 155 * the resource is considered invalid for user access or allocation or use in other 156 * resources. 157 * 158 * <p>References to the IResource object may be held by other RefcountedResource objects, 159 * and as such, the underlying resources and quota may not be cleaned up. 160 */ invalidate()161 void invalidate() throws RemoteException; 162 163 /** 164 * Releases underlying resources and related quotas. 165 * 166 * <p>Implementations of this method are expected to remove all system resources that are 167 * tracked by the IResource object. Due to other RefcountedResource objects potentially 168 * having references to the IResource object, freeUnderlyingResources may not always be 169 * called from releaseIfUnreferencedRecursively(). 170 */ freeUnderlyingResources()171 void freeUnderlyingResources() throws RemoteException; 172 } 173 174 /** 175 * RefcountedResource manages references and dependencies in an exclusively acyclic graph. 176 * 177 * <p>RefcountedResource implements both explicit and implicit resource management. Creating a 178 * RefcountedResource object creates an explicit reference that must be freed by calling 179 * userRelease(). Additionally, adding this object as a child of another RefcountedResource 180 * object will add an implicit reference. 181 * 182 * <p>Resources are cleaned up when all references, both implicit and explicit, are released 183 * (ie, when userRelease() is called and when all parents have called releaseReference() on this 184 * object.) 185 */ 186 @VisibleForTesting 187 public class RefcountedResource<T extends IResource> implements IBinder.DeathRecipient { 188 private final T mResource; 189 private final List<RefcountedResource> mChildren; 190 int mRefCount = 1; // starts at 1 for user's reference. 191 IBinder mBinder; 192 RefcountedResource(T resource, IBinder binder, RefcountedResource... children)193 RefcountedResource(T resource, IBinder binder, RefcountedResource... children) { 194 synchronized (IpSecService.this) { 195 this.mResource = resource; 196 this.mChildren = new ArrayList<>(children.length); 197 this.mBinder = binder; 198 199 for (RefcountedResource child : children) { 200 mChildren.add(child); 201 child.mRefCount++; 202 } 203 204 try { 205 mBinder.linkToDeath(this, 0); 206 } catch (RemoteException e) { 207 binderDied(); 208 } 209 } 210 } 211 212 /** 213 * If the Binder object dies, this function is called to free the system resources that are 214 * being tracked by this record and to subsequently release this record for garbage 215 * collection 216 */ 217 @Override binderDied()218 public void binderDied() { 219 synchronized (IpSecService.this) { 220 try { 221 userRelease(); 222 } catch (Exception e) { 223 Log.e(TAG, "Failed to release resource: " + e); 224 } 225 } 226 } 227 getResource()228 public T getResource() { 229 return mResource; 230 } 231 232 /** 233 * Unlinks from binder and performs IpSecService resource cleanup (removes from resource 234 * arrays) 235 * 236 * <p>If this method has been previously called, the RefcountedResource's binder field will 237 * be null, and the method will return without performing the cleanup a second time. 238 * 239 * <p>Note that calling this function does not imply that kernel resources will be freed at 240 * this time, or that the related quota will be returned. Such actions will only be 241 * performed upon the reference count reaching zero. 242 */ 243 @GuardedBy("IpSecService.this") userRelease()244 public void userRelease() throws RemoteException { 245 // Prevent users from putting reference counts into a bad state by calling 246 // userRelease() multiple times. 247 if (mBinder == null) { 248 return; 249 } 250 251 mBinder.unlinkToDeath(this, 0); 252 mBinder = null; 253 254 mResource.invalidate(); 255 256 releaseReference(); 257 } 258 259 /** 260 * Removes a reference to this resource. If the resultant reference count is zero, the 261 * underlying resources are freed, and references to all child resources are also dropped 262 * recursively (resulting in them freeing their resources and children, etcetera) 263 * 264 * <p>This method also sets the reference count to an invalid value (-1) to signify that it 265 * has been fully released. Any subsequent calls to this method will result in an 266 * IllegalStateException being thrown due to resource already having been previously 267 * released 268 */ 269 @VisibleForTesting 270 @GuardedBy("IpSecService.this") releaseReference()271 public void releaseReference() throws RemoteException { 272 mRefCount--; 273 274 if (mRefCount > 0) { 275 return; 276 } else if (mRefCount < 0) { 277 throw new IllegalStateException( 278 "Invalid operation - resource has already been released."); 279 } 280 281 // Cleanup own resources 282 mResource.freeUnderlyingResources(); 283 284 // Cleanup child resources as needed 285 for (RefcountedResource<? extends IResource> child : mChildren) { 286 child.releaseReference(); 287 } 288 289 // Enforce that resource cleanup can only be called once 290 // By decrementing the refcount (from 0 to -1), the next call will throw an 291 // IllegalStateException - it has already been released fully. 292 mRefCount--; 293 } 294 295 @Override toString()296 public String toString() { 297 return new StringBuilder() 298 .append("{mResource=") 299 .append(mResource) 300 .append(", mRefCount=") 301 .append(mRefCount) 302 .append(", mChildren=") 303 .append(mChildren) 304 .append("}") 305 .toString(); 306 } 307 } 308 309 /** 310 * Very simple counting class that looks much like a counting semaphore 311 * 312 * <p>This class is not thread-safe, and expects that that users of this class will ensure 313 * synchronization and thread safety by holding the IpSecService.this instance lock. 314 */ 315 @VisibleForTesting 316 static class ResourceTracker { 317 private final int mMax; 318 int mCurrent; 319 ResourceTracker(int max)320 ResourceTracker(int max) { 321 mMax = max; 322 mCurrent = 0; 323 } 324 isAvailable()325 boolean isAvailable() { 326 return (mCurrent < mMax); 327 } 328 take()329 void take() { 330 if (!isAvailable()) { 331 Log.wtf(TAG, "Too many resources allocated!"); 332 } 333 mCurrent++; 334 } 335 give()336 void give() { 337 if (mCurrent <= 0) { 338 Log.wtf(TAG, "We've released this resource too many times"); 339 } 340 mCurrent--; 341 } 342 343 @Override toString()344 public String toString() { 345 return new StringBuilder() 346 .append("{mCurrent=") 347 .append(mCurrent) 348 .append(", mMax=") 349 .append(mMax) 350 .append("}") 351 .toString(); 352 } 353 } 354 355 @VisibleForTesting 356 static final class UserRecord { 357 /* Maximum number of each type of resource that a single UID may possess */ 358 public static final int MAX_NUM_TUNNEL_INTERFACES = 2; 359 public static final int MAX_NUM_ENCAP_SOCKETS = 2; 360 public static final int MAX_NUM_TRANSFORMS = 4; 361 public static final int MAX_NUM_SPIS = 8; 362 363 /** 364 * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing 365 * and explicit (user) reference management. 366 * 367 * <p>These are stored in separate arrays to improve debuggability and dump output clarity. 368 * 369 * <p>Resources are removed from this array when the user releases their explicit reference 370 * by calling one of the releaseResource() methods. 371 */ 372 final RefcountedResourceArray<SpiRecord> mSpiRecords = 373 new RefcountedResourceArray<>(SpiRecord.class.getSimpleName()); 374 final RefcountedResourceArray<TransformRecord> mTransformRecords = 375 new RefcountedResourceArray<>(TransformRecord.class.getSimpleName()); 376 final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords = 377 new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName()); 378 final RefcountedResourceArray<TunnelInterfaceRecord> mTunnelInterfaceRecords = 379 new RefcountedResourceArray<>(TunnelInterfaceRecord.class.getSimpleName()); 380 381 /** 382 * Trackers for quotas for each of the OwnedResource types. 383 * 384 * <p>These trackers are separate from the resource arrays, since they are incremented and 385 * decremented at different points in time. Specifically, quota is only returned upon final 386 * resource deallocation (after all explicit and implicit references are released). Note 387 * that it is possible that calls to releaseResource() will not return the used quota if 388 * there are other resources that depend on (are parents of) the resource being released. 389 */ 390 final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS); 391 final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS); 392 final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS); 393 final ResourceTracker mTunnelQuotaTracker = new ResourceTracker(MAX_NUM_TUNNEL_INTERFACES); 394 removeSpiRecord(int resourceId)395 void removeSpiRecord(int resourceId) { 396 mSpiRecords.remove(resourceId); 397 } 398 removeTransformRecord(int resourceId)399 void removeTransformRecord(int resourceId) { 400 mTransformRecords.remove(resourceId); 401 } 402 removeTunnelInterfaceRecord(int resourceId)403 void removeTunnelInterfaceRecord(int resourceId) { 404 mTunnelInterfaceRecords.remove(resourceId); 405 } 406 removeEncapSocketRecord(int resourceId)407 void removeEncapSocketRecord(int resourceId) { 408 mEncapSocketRecords.remove(resourceId); 409 } 410 411 @Override toString()412 public String toString() { 413 return new StringBuilder() 414 .append("{mSpiQuotaTracker=") 415 .append(mSpiQuotaTracker) 416 .append(", mTransformQuotaTracker=") 417 .append(mTransformQuotaTracker) 418 .append(", mSocketQuotaTracker=") 419 .append(mSocketQuotaTracker) 420 .append(", mTunnelQuotaTracker=") 421 .append(mTunnelQuotaTracker) 422 .append(", mSpiRecords=") 423 .append(mSpiRecords) 424 .append(", mTransformRecords=") 425 .append(mTransformRecords) 426 .append(", mEncapSocketRecords=") 427 .append(mEncapSocketRecords) 428 .append(", mTunnelInterfaceRecords=") 429 .append(mTunnelInterfaceRecords) 430 .append("}") 431 .toString(); 432 } 433 } 434 435 /** 436 * This class is not thread-safe, and expects that that users of this class will ensure 437 * synchronization and thread safety by holding the IpSecService.this instance lock. 438 */ 439 @VisibleForTesting 440 static final class UserResourceTracker { 441 private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); 442 443 /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */ getUserRecord(int uid)444 public UserRecord getUserRecord(int uid) { 445 checkCallerUid(uid); 446 447 UserRecord r = mUserRecords.get(uid); 448 if (r == null) { 449 r = new UserRecord(); 450 mUserRecords.put(uid, r); 451 } 452 return r; 453 } 454 455 /** Safety method; guards against access of other user's UserRecords */ checkCallerUid(int uid)456 private void checkCallerUid(int uid) { 457 if (uid != Binder.getCallingUid() 458 && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) { 459 throw new SecurityException("Attempted access of unowned resources"); 460 } 461 } 462 463 @Override toString()464 public String toString() { 465 return mUserRecords.toString(); 466 } 467 } 468 469 @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker(); 470 471 /** 472 * The OwnedResourceRecord class provides a facility to cleanly and reliably track system 473 * resources. It relies on a provided resourceId that should uniquely identify the kernel 474 * resource. To use this class, the user should implement the invalidate() and 475 * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource 476 * tracking arrays and kernel resources, respectively. 477 * 478 * <p>This class associates kernel resources with the UID that owns and controls them. 479 */ 480 private abstract class OwnedResourceRecord implements IResource { 481 final int pid; 482 final int uid; 483 protected final int mResourceId; 484 OwnedResourceRecord(int resourceId)485 OwnedResourceRecord(int resourceId) { 486 super(); 487 if (resourceId == INVALID_RESOURCE_ID) { 488 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID"); 489 } 490 mResourceId = resourceId; 491 pid = Binder.getCallingPid(); 492 uid = Binder.getCallingUid(); 493 494 getResourceTracker().take(); 495 } 496 497 @Override invalidate()498 public abstract void invalidate() throws RemoteException; 499 500 /** Convenience method; retrieves the user resource record for the stored UID. */ getUserRecord()501 protected UserRecord getUserRecord() { 502 return mUserResourceTracker.getUserRecord(uid); 503 } 504 505 @Override freeUnderlyingResources()506 public abstract void freeUnderlyingResources() throws RemoteException; 507 508 /** Get the resource tracker for this resource */ getResourceTracker()509 protected abstract ResourceTracker getResourceTracker(); 510 511 @Override toString()512 public String toString() { 513 return new StringBuilder() 514 .append("{mResourceId=") 515 .append(mResourceId) 516 .append(", pid=") 517 .append(pid) 518 .append(", uid=") 519 .append(uid) 520 .append("}") 521 .toString(); 522 } 523 }; 524 525 /** 526 * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing. 527 * 528 * <p>RefcountedResourceArray prevents null insertions, and throws an IllegalArgumentException 529 * if a key is not found during a retrieval process. 530 */ 531 static class RefcountedResourceArray<T extends IResource> { 532 SparseArray<RefcountedResource<T>> mArray = new SparseArray<>(); 533 private final String mTypeName; 534 RefcountedResourceArray(String typeName)535 public RefcountedResourceArray(String typeName) { 536 this.mTypeName = typeName; 537 } 538 539 /** 540 * Accessor method to get inner resource object. 541 * 542 * @throws IllegalArgumentException if no resource with provided key is found. 543 */ getResourceOrThrow(int key)544 T getResourceOrThrow(int key) { 545 return getRefcountedResourceOrThrow(key).getResource(); 546 } 547 548 /** 549 * Accessor method to get reference counting wrapper. 550 * 551 * @throws IllegalArgumentException if no resource with provided key is found. 552 */ getRefcountedResourceOrThrow(int key)553 RefcountedResource<T> getRefcountedResourceOrThrow(int key) { 554 RefcountedResource<T> resource = mArray.get(key); 555 if (resource == null) { 556 throw new IllegalArgumentException( 557 String.format("No such %s found for given id: %d", mTypeName, key)); 558 } 559 560 return resource; 561 } 562 put(int key, RefcountedResource<T> obj)563 void put(int key, RefcountedResource<T> obj) { 564 checkNotNull(obj, "Null resources cannot be added"); 565 mArray.put(key, obj); 566 } 567 remove(int key)568 void remove(int key) { 569 mArray.remove(key); 570 } 571 572 @Override toString()573 public String toString() { 574 return mArray.toString(); 575 } 576 } 577 578 /** 579 * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is 580 * created, the SpiRecord that originally tracked the SAs will reliquish the 581 * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag. 582 */ 583 private final class TransformRecord extends OwnedResourceRecord { 584 private final IpSecConfig mConfig; 585 private final SpiRecord mSpi; 586 private final EncapSocketRecord mSocket; 587 TransformRecord( int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket)588 TransformRecord( 589 int resourceId, IpSecConfig config, SpiRecord spi, EncapSocketRecord socket) { 590 super(resourceId); 591 mConfig = config; 592 mSpi = spi; 593 mSocket = socket; 594 595 spi.setOwnedByTransform(); 596 } 597 getConfig()598 public IpSecConfig getConfig() { 599 return mConfig; 600 } 601 getSpiRecord()602 public SpiRecord getSpiRecord() { 603 return mSpi; 604 } 605 getSocketRecord()606 public EncapSocketRecord getSocketRecord() { 607 return mSocket; 608 } 609 610 /** always guarded by IpSecService#this */ 611 @Override freeUnderlyingResources()612 public void freeUnderlyingResources() { 613 int spi = mSpi.getSpi(); 614 try { 615 mSrvConfig 616 .getNetdInstance() 617 .ipSecDeleteSecurityAssociation( 618 mResourceId, 619 mConfig.getSourceAddress(), 620 mConfig.getDestinationAddress(), 621 spi, 622 mConfig.getMarkValue(), 623 mConfig.getMarkMask()); 624 } catch (RemoteException | ServiceSpecificException e) { 625 Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e); 626 } 627 628 getResourceTracker().give(); 629 } 630 631 @Override invalidate()632 public void invalidate() throws RemoteException { 633 getUserRecord().removeTransformRecord(mResourceId); 634 } 635 636 @Override getResourceTracker()637 protected ResourceTracker getResourceTracker() { 638 return getUserRecord().mTransformQuotaTracker; 639 } 640 641 @Override toString()642 public String toString() { 643 StringBuilder strBuilder = new StringBuilder(); 644 strBuilder 645 .append("{super=") 646 .append(super.toString()) 647 .append(", mSocket=") 648 .append(mSocket) 649 .append(", mSpi.mResourceId=") 650 .append(mSpi.mResourceId) 651 .append(", mConfig=") 652 .append(mConfig) 653 .append("}"); 654 return strBuilder.toString(); 655 } 656 } 657 658 /** 659 * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the 660 * responsibility for cleaning up underlying resources will be passed to the TransformRecord 661 * object 662 */ 663 private final class SpiRecord extends OwnedResourceRecord { 664 private final String mSourceAddress; 665 private final String mDestinationAddress; 666 private int mSpi; 667 668 private boolean mOwnedByTransform = false; 669 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi)670 SpiRecord(int resourceId, String sourceAddress, String destinationAddress, int spi) { 671 super(resourceId); 672 mSourceAddress = sourceAddress; 673 mDestinationAddress = destinationAddress; 674 mSpi = spi; 675 } 676 677 /** always guarded by IpSecService#this */ 678 @Override freeUnderlyingResources()679 public void freeUnderlyingResources() { 680 try { 681 if (!mOwnedByTransform) { 682 mSrvConfig 683 .getNetdInstance() 684 .ipSecDeleteSecurityAssociation( 685 mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0); 686 } 687 } catch (ServiceSpecificException | RemoteException e) { 688 Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e); 689 } 690 691 mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; 692 693 getResourceTracker().give(); 694 } 695 getSpi()696 public int getSpi() { 697 return mSpi; 698 } 699 getDestinationAddress()700 public String getDestinationAddress() { 701 return mDestinationAddress; 702 } 703 setOwnedByTransform()704 public void setOwnedByTransform() { 705 if (mOwnedByTransform) { 706 // Programming error 707 throw new IllegalStateException("Cannot own an SPI twice!"); 708 } 709 710 mOwnedByTransform = true; 711 } 712 getOwnedByTransform()713 public boolean getOwnedByTransform() { 714 return mOwnedByTransform; 715 } 716 717 @Override invalidate()718 public void invalidate() throws RemoteException { 719 getUserRecord().removeSpiRecord(mResourceId); 720 } 721 722 @Override getResourceTracker()723 protected ResourceTracker getResourceTracker() { 724 return getUserRecord().mSpiQuotaTracker; 725 } 726 727 @Override toString()728 public String toString() { 729 StringBuilder strBuilder = new StringBuilder(); 730 strBuilder 731 .append("{super=") 732 .append(super.toString()) 733 .append(", mSpi=") 734 .append(mSpi) 735 .append(", mSourceAddress=") 736 .append(mSourceAddress) 737 .append(", mDestinationAddress=") 738 .append(mDestinationAddress) 739 .append(", mOwnedByTransform=") 740 .append(mOwnedByTransform) 741 .append("}"); 742 return strBuilder.toString(); 743 } 744 } 745 746 // These values have been reserved in ConnectivityService 747 @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00; 748 749 @VisibleForTesting static final int TUN_INTF_NETID_RANGE = 0x0400; 750 751 private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray(); 752 private int mNextTunnelNetIdIndex = 0; 753 754 /** 755 * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces 756 * 757 * <p>This method should only be called from Binder threads. Do not call this from within the 758 * system server as it will crash the system on failure. 759 * 760 * @return an integer key within the netId range, if successful 761 * @throws IllegalStateException if unsuccessful (all netId are currently reserved) 762 */ 763 @VisibleForTesting reserveNetId()764 int reserveNetId() { 765 synchronized (mTunnelNetIds) { 766 for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) { 767 int index = mNextTunnelNetIdIndex; 768 int netId = index + TUN_INTF_NETID_START; 769 if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0; 770 if (!mTunnelNetIds.get(netId)) { 771 mTunnelNetIds.put(netId, true); 772 return netId; 773 } 774 } 775 } 776 throw new IllegalStateException("No free netIds to allocate"); 777 } 778 779 @VisibleForTesting releaseNetId(int netId)780 void releaseNetId(int netId) { 781 synchronized (mTunnelNetIds) { 782 mTunnelNetIds.delete(netId); 783 } 784 } 785 786 private final class TunnelInterfaceRecord extends OwnedResourceRecord { 787 private final String mInterfaceName; 788 private final Network mUnderlyingNetwork; 789 790 // outer addresses 791 private final String mLocalAddress; 792 private final String mRemoteAddress; 793 794 private final int mIkey; 795 private final int mOkey; 796 TunnelInterfaceRecord( int resourceId, String interfaceName, Network underlyingNetwork, String localAddr, String remoteAddr, int ikey, int okey)797 TunnelInterfaceRecord( 798 int resourceId, 799 String interfaceName, 800 Network underlyingNetwork, 801 String localAddr, 802 String remoteAddr, 803 int ikey, 804 int okey) { 805 super(resourceId); 806 807 mInterfaceName = interfaceName; 808 mUnderlyingNetwork = underlyingNetwork; 809 mLocalAddress = localAddr; 810 mRemoteAddress = remoteAddr; 811 mIkey = ikey; 812 mOkey = okey; 813 } 814 815 /** always guarded by IpSecService#this */ 816 @Override freeUnderlyingResources()817 public void freeUnderlyingResources() { 818 // Calls to netd 819 // Teardown VTI 820 // Delete global policies 821 try { 822 mSrvConfig.getNetdInstance().removeVirtualTunnelInterface(mInterfaceName); 823 824 for(String wildcardAddr : WILDCARD_ADDRESSES) { 825 for (int direction : DIRECTIONS) { 826 int mark = (direction == IpSecManager.DIRECTION_IN) ? mIkey : mOkey; 827 mSrvConfig 828 .getNetdInstance() 829 .ipSecDeleteSecurityPolicy( 830 0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff); 831 } 832 } 833 } catch (ServiceSpecificException | RemoteException e) { 834 Log.e( 835 TAG, 836 "Failed to delete VTI with interface name: " 837 + mInterfaceName 838 + " and id: " 839 + mResourceId, e); 840 } 841 842 getResourceTracker().give(); 843 releaseNetId(mIkey); 844 releaseNetId(mOkey); 845 } 846 getInterfaceName()847 public String getInterfaceName() { 848 return mInterfaceName; 849 } 850 getUnderlyingNetwork()851 public Network getUnderlyingNetwork() { 852 return mUnderlyingNetwork; 853 } 854 855 /** Returns the local, outer address for the tunnelInterface */ getLocalAddress()856 public String getLocalAddress() { 857 return mLocalAddress; 858 } 859 860 /** Returns the remote, outer address for the tunnelInterface */ getRemoteAddress()861 public String getRemoteAddress() { 862 return mRemoteAddress; 863 } 864 getIkey()865 public int getIkey() { 866 return mIkey; 867 } 868 getOkey()869 public int getOkey() { 870 return mOkey; 871 } 872 873 @Override getResourceTracker()874 protected ResourceTracker getResourceTracker() { 875 return getUserRecord().mTunnelQuotaTracker; 876 } 877 878 @Override invalidate()879 public void invalidate() { 880 getUserRecord().removeTunnelInterfaceRecord(mResourceId); 881 } 882 883 @Override toString()884 public String toString() { 885 return new StringBuilder() 886 .append("{super=") 887 .append(super.toString()) 888 .append(", mInterfaceName=") 889 .append(mInterfaceName) 890 .append(", mUnderlyingNetwork=") 891 .append(mUnderlyingNetwork) 892 .append(", mLocalAddress=") 893 .append(mLocalAddress) 894 .append(", mRemoteAddress=") 895 .append(mRemoteAddress) 896 .append(", mIkey=") 897 .append(mIkey) 898 .append(", mOkey=") 899 .append(mOkey) 900 .append("}") 901 .toString(); 902 } 903 } 904 905 /** 906 * Tracks a UDP encap socket, and manages cleanup paths 907 * 908 * <p>While this class does not manage non-kernel resources, race conditions around socket 909 * binding require that the service creates the encap socket, binds it and applies the socket 910 * policy before handing it to a user. 911 */ 912 private final class EncapSocketRecord extends OwnedResourceRecord { 913 private FileDescriptor mSocket; 914 private final int mPort; 915 EncapSocketRecord(int resourceId, FileDescriptor socket, int port)916 EncapSocketRecord(int resourceId, FileDescriptor socket, int port) { 917 super(resourceId); 918 mSocket = socket; 919 mPort = port; 920 } 921 922 /** always guarded by IpSecService#this */ 923 @Override freeUnderlyingResources()924 public void freeUnderlyingResources() { 925 Log.d(TAG, "Closing port " + mPort); 926 IoUtils.closeQuietly(mSocket); 927 mSocket = null; 928 929 getResourceTracker().give(); 930 } 931 getPort()932 public int getPort() { 933 return mPort; 934 } 935 getFileDescriptor()936 public FileDescriptor getFileDescriptor() { 937 return mSocket; 938 } 939 940 @Override getResourceTracker()941 protected ResourceTracker getResourceTracker() { 942 return getUserRecord().mSocketQuotaTracker; 943 } 944 945 @Override invalidate()946 public void invalidate() { 947 getUserRecord().removeEncapSocketRecord(mResourceId); 948 } 949 950 @Override toString()951 public String toString() { 952 return new StringBuilder() 953 .append("{super=") 954 .append(super.toString()) 955 .append(", mSocket=") 956 .append(mSocket) 957 .append(", mPort=") 958 .append(mPort) 959 .append("}") 960 .toString(); 961 } 962 } 963 964 /** 965 * Constructs a new IpSecService instance 966 * 967 * @param context Binder context for this service 968 */ IpSecService(Context context)969 private IpSecService(Context context) { 970 this(context, IpSecServiceConfiguration.GETSRVINSTANCE); 971 } 972 create(Context context)973 static IpSecService create(Context context) throws InterruptedException { 974 final IpSecService service = new IpSecService(context); 975 service.connectNativeNetdService(); 976 return service; 977 } 978 979 @NonNull getAppOpsManager()980 private AppOpsManager getAppOpsManager() { 981 AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 982 if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps"); 983 return appOps; 984 } 985 986 /** @hide */ 987 @VisibleForTesting IpSecService(Context context, IpSecServiceConfiguration config)988 public IpSecService(Context context, IpSecServiceConfiguration config) { 989 this( 990 context, 991 config, 992 (fd, uid) -> { 993 try { 994 TrafficStats.setThreadStatsUid(uid); 995 TrafficStats.tagFileDescriptor(fd); 996 } finally { 997 TrafficStats.clearThreadStatsUid(); 998 } 999 }); 1000 } 1001 1002 /** @hide */ 1003 @VisibleForTesting IpSecService( Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger)1004 public IpSecService( 1005 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) { 1006 mContext = context; 1007 mSrvConfig = config; 1008 mUidFdTagger = uidFdTagger; 1009 } 1010 systemReady()1011 public void systemReady() { 1012 if (isNetdAlive()) { 1013 Slog.d(TAG, "IpSecService is ready"); 1014 } else { 1015 Slog.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!"); 1016 } 1017 } 1018 connectNativeNetdService()1019 private void connectNativeNetdService() { 1020 // Avoid blocking the system server to do this 1021 new Thread() { 1022 @Override 1023 public void run() { 1024 synchronized (IpSecService.this) { 1025 NetdService.get(NETD_FETCH_TIMEOUT_MS); 1026 } 1027 } 1028 }.start(); 1029 } 1030 isNetdAlive()1031 synchronized boolean isNetdAlive() { 1032 try { 1033 final INetd netd = mSrvConfig.getNetdInstance(); 1034 if (netd == null) { 1035 return false; 1036 } 1037 return netd.isAlive(); 1038 } catch (RemoteException re) { 1039 return false; 1040 } 1041 } 1042 1043 /** 1044 * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be 1045 * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1. 1046 */ checkInetAddress(String inetAddress)1047 private static void checkInetAddress(String inetAddress) { 1048 if (TextUtils.isEmpty(inetAddress)) { 1049 throw new IllegalArgumentException("Unspecified address"); 1050 } 1051 1052 InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress); 1053 1054 if (checkAddr.isAnyLocalAddress()) { 1055 throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress); 1056 } 1057 } 1058 1059 /** 1060 * Checks the user-provided direction field and throws an IllegalArgumentException if it is not 1061 * DIRECTION_IN or DIRECTION_OUT 1062 */ checkDirection(int direction)1063 private static void checkDirection(int direction) { 1064 switch (direction) { 1065 case IpSecManager.DIRECTION_OUT: 1066 case IpSecManager.DIRECTION_IN: 1067 return; 1068 } 1069 throw new IllegalArgumentException("Invalid Direction: " + direction); 1070 } 1071 1072 /** Get a new SPI and maintain the reservation in the system server */ 1073 @Override allocateSecurityParameterIndex( String destinationAddress, int requestedSpi, IBinder binder)1074 public synchronized IpSecSpiResponse allocateSecurityParameterIndex( 1075 String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException { 1076 checkInetAddress(destinationAddress); 1077 // RFC 4303 Section 2.1 - 0=local, 1-255=reserved. 1078 if (requestedSpi > 0 && requestedSpi < 256) { 1079 throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255."); 1080 } 1081 checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex"); 1082 1083 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1084 final int resourceId = mNextResourceId++; 1085 1086 int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; 1087 try { 1088 if (!userRecord.mSpiQuotaTracker.isAvailable()) { 1089 return new IpSecSpiResponse( 1090 IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi); 1091 } 1092 1093 spi = 1094 mSrvConfig 1095 .getNetdInstance() 1096 .ipSecAllocateSpi(resourceId, "", destinationAddress, requestedSpi); 1097 Log.d(TAG, "Allocated SPI " + spi); 1098 userRecord.mSpiRecords.put( 1099 resourceId, 1100 new RefcountedResource<SpiRecord>( 1101 new SpiRecord(resourceId, "", destinationAddress, spi), binder)); 1102 } catch (ServiceSpecificException e) { 1103 if (e.errorCode == OsConstants.ENOENT) { 1104 return new IpSecSpiResponse( 1105 IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi); 1106 } 1107 throw e; 1108 } catch (RemoteException e) { 1109 throw e.rethrowFromSystemServer(); 1110 } 1111 return new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, spi); 1112 } 1113 1114 /* This method should only be called from Binder threads. Do not call this from 1115 * within the system server as it will crash the system on failure. 1116 */ releaseResource(RefcountedResourceArray resArray, int resourceId)1117 private void releaseResource(RefcountedResourceArray resArray, int resourceId) 1118 throws RemoteException { 1119 resArray.getRefcountedResourceOrThrow(resourceId).userRelease(); 1120 } 1121 1122 /** Release a previously allocated SPI that has been registered with the system server */ 1123 @Override releaseSecurityParameterIndex(int resourceId)1124 public synchronized void releaseSecurityParameterIndex(int resourceId) throws RemoteException { 1125 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1126 releaseResource(userRecord.mSpiRecords, resourceId); 1127 } 1128 1129 /** 1130 * This function finds and forcibly binds to a random system port, ensuring that the port cannot 1131 * be unbound. 1132 * 1133 * <p>A socket cannot be un-bound from a port if it was bound to that port by number. To select 1134 * a random open port and then bind by number, this function creates a temp socket, binds to a 1135 * random port (specifying 0), gets that port number, and then uses is to bind the user's UDP 1136 * Encapsulation Socket forcibly, so that it cannot be un-bound by the user with the returned 1137 * FileHandle. 1138 * 1139 * <p>The loop in this function handles the inherent race window between un-binding to a port 1140 * and re-binding, during which the system could *technically* hand that port out to someone 1141 * else. 1142 */ bindToRandomPort(FileDescriptor sockFd)1143 private int bindToRandomPort(FileDescriptor sockFd) throws IOException { 1144 for (int i = MAX_PORT_BIND_ATTEMPTS; i > 0; i--) { 1145 try { 1146 FileDescriptor probeSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 1147 Os.bind(probeSocket, INADDR_ANY, 0); 1148 int port = ((InetSocketAddress) Os.getsockname(probeSocket)).getPort(); 1149 Os.close(probeSocket); 1150 Log.v(TAG, "Binding to port " + port); 1151 Os.bind(sockFd, INADDR_ANY, port); 1152 return port; 1153 } catch (ErrnoException e) { 1154 // Someone miraculously claimed the port just after we closed probeSocket. 1155 if (e.errno == OsConstants.EADDRINUSE) { 1156 continue; 1157 } 1158 throw e.rethrowAsIOException(); 1159 } 1160 } 1161 throw new IOException("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port"); 1162 } 1163 1164 /** 1165 * Functional interface to do traffic tagging of given sockets to UIDs. 1166 * 1167 * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap 1168 * sockets are billed to the UID that the UDP encap socket was created on behalf of. 1169 * 1170 * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static 1171 * methods that cannot be easily mocked/tested. 1172 */ 1173 @VisibleForTesting 1174 public interface UidFdTagger { 1175 /** 1176 * Sets socket tag to assign all traffic to the provided UID. 1177 * 1178 * <p>Since the socket is created on behalf of an unprivileged application, all traffic 1179 * should be accounted to the UID of the unprivileged application. 1180 */ tag(FileDescriptor fd, int uid)1181 public void tag(FileDescriptor fd, int uid) throws IOException; 1182 } 1183 1184 /** 1185 * Open a socket via the system server and bind it to the specified port (random if port=0). 1186 * This will return a PFD to the user that represent a bound UDP socket. The system server will 1187 * cache the socket and a record of its owner so that it can and must be freed when no longer 1188 * needed. 1189 */ 1190 @Override openUdpEncapsulationSocket(int port, IBinder binder)1191 public synchronized IpSecUdpEncapResponse openUdpEncapsulationSocket(int port, IBinder binder) 1192 throws RemoteException { 1193 if (port != 0 && (port < FREE_PORT_MIN || port > PORT_MAX)) { 1194 throw new IllegalArgumentException( 1195 "Specified port number must be a valid non-reserved UDP port"); 1196 } 1197 checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket"); 1198 1199 int callingUid = Binder.getCallingUid(); 1200 UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid); 1201 final int resourceId = mNextResourceId++; 1202 FileDescriptor sockFd = null; 1203 try { 1204 if (!userRecord.mSocketQuotaTracker.isAvailable()) { 1205 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); 1206 } 1207 1208 sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 1209 mUidFdTagger.tag(sockFd, callingUid); 1210 1211 // This code is common to both the unspecified and specified port cases 1212 Os.setsockoptInt( 1213 sockFd, 1214 OsConstants.IPPROTO_UDP, 1215 OsConstants.UDP_ENCAP, 1216 OsConstants.UDP_ENCAP_ESPINUDP); 1217 1218 mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid); 1219 if (port != 0) { 1220 Log.v(TAG, "Binding to port " + port); 1221 Os.bind(sockFd, INADDR_ANY, port); 1222 } else { 1223 port = bindToRandomPort(sockFd); 1224 } 1225 1226 userRecord.mEncapSocketRecords.put( 1227 resourceId, 1228 new RefcountedResource<EncapSocketRecord>( 1229 new EncapSocketRecord(resourceId, sockFd, port), binder)); 1230 return new IpSecUdpEncapResponse(IpSecManager.Status.OK, resourceId, port, sockFd); 1231 } catch (IOException | ErrnoException e) { 1232 IoUtils.closeQuietly(sockFd); 1233 } 1234 // If we make it to here, then something has gone wrong and we couldn't open a socket. 1235 // The only reasonable condition that would cause that is resource unavailable. 1236 return new IpSecUdpEncapResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); 1237 } 1238 1239 /** close a socket that has been been allocated by and registered with the system server */ 1240 @Override closeUdpEncapsulationSocket(int resourceId)1241 public synchronized void closeUdpEncapsulationSocket(int resourceId) throws RemoteException { 1242 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1243 releaseResource(userRecord.mEncapSocketRecords, resourceId); 1244 } 1245 1246 /** 1247 * Create a tunnel interface for use in IPSec tunnel mode. The system server will cache the 1248 * tunnel interface and a record of its owner so that it can and must be freed when no longer 1249 * needed. 1250 */ 1251 @Override createTunnelInterface( String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder, String callingPackage)1252 public synchronized IpSecTunnelInterfaceResponse createTunnelInterface( 1253 String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder, 1254 String callingPackage) { 1255 enforceTunnelPermissions(callingPackage); 1256 checkNotNull(binder, "Null Binder passed to createTunnelInterface"); 1257 checkNotNull(underlyingNetwork, "No underlying network was specified"); 1258 checkInetAddress(localAddr); 1259 checkInetAddress(remoteAddr); 1260 1261 // TODO: Check that underlying network exists, and IP addresses not assigned to a different 1262 // network (b/72316676). 1263 1264 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1265 if (!userRecord.mTunnelQuotaTracker.isAvailable()) { 1266 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); 1267 } 1268 1269 final int resourceId = mNextResourceId++; 1270 final int ikey = reserveNetId(); 1271 final int okey = reserveNetId(); 1272 String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId); 1273 1274 try { 1275 // Calls to netd: 1276 // Create VTI 1277 // Add inbound/outbound global policies 1278 // (use reqid = 0) 1279 mSrvConfig 1280 .getNetdInstance() 1281 .addVirtualTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey); 1282 1283 for(String wildcardAddr : WILDCARD_ADDRESSES) { 1284 for (int direction : DIRECTIONS) { 1285 int mark = (direction == IpSecManager.DIRECTION_OUT) ? okey : ikey; 1286 1287 mSrvConfig 1288 .getNetdInstance() 1289 .ipSecAddSecurityPolicy( 1290 0, // Use 0 for reqId 1291 direction, 1292 wildcardAddr, 1293 wildcardAddr, 1294 0, 1295 mark, 1296 0xffffffff); 1297 } 1298 } 1299 1300 userRecord.mTunnelInterfaceRecords.put( 1301 resourceId, 1302 new RefcountedResource<TunnelInterfaceRecord>( 1303 new TunnelInterfaceRecord( 1304 resourceId, 1305 intfName, 1306 underlyingNetwork, 1307 localAddr, 1308 remoteAddr, 1309 ikey, 1310 okey), 1311 binder)); 1312 return new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName); 1313 } catch (RemoteException e) { 1314 // Release keys if we got an error. 1315 releaseNetId(ikey); 1316 releaseNetId(okey); 1317 throw e.rethrowFromSystemServer(); 1318 } catch (Throwable t) { 1319 // Release keys if we got an error. 1320 releaseNetId(ikey); 1321 releaseNetId(okey); 1322 throw t; 1323 } 1324 } 1325 1326 /** 1327 * Adds a new local address to the tunnel interface. This allows packets to be sent and received 1328 * from multiple local IP addresses over the same tunnel. 1329 */ 1330 @Override addAddressToTunnelInterface( int tunnelResourceId, LinkAddress localAddr, String callingPackage)1331 public synchronized void addAddressToTunnelInterface( 1332 int tunnelResourceId, LinkAddress localAddr, String callingPackage) { 1333 enforceTunnelPermissions(callingPackage); 1334 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1335 1336 // Get tunnelInterface record; if no such interface is found, will throw 1337 // IllegalArgumentException 1338 TunnelInterfaceRecord tunnelInterfaceInfo = 1339 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); 1340 1341 try { 1342 // We can assume general validity of the IP address, since we get them as a 1343 // LinkAddress, which does some validation. 1344 mSrvConfig 1345 .getNetdInstance() 1346 .interfaceAddAddress( 1347 tunnelInterfaceInfo.mInterfaceName, 1348 localAddr.getAddress().getHostAddress(), 1349 localAddr.getPrefixLength()); 1350 } catch (RemoteException e) { 1351 throw e.rethrowFromSystemServer(); 1352 } 1353 } 1354 1355 /** 1356 * Remove a new local address from the tunnel interface. After removal, the address will no 1357 * longer be available to send from, or receive on. 1358 */ 1359 @Override removeAddressFromTunnelInterface( int tunnelResourceId, LinkAddress localAddr, String callingPackage)1360 public synchronized void removeAddressFromTunnelInterface( 1361 int tunnelResourceId, LinkAddress localAddr, String callingPackage) { 1362 enforceTunnelPermissions(callingPackage); 1363 1364 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1365 // Get tunnelInterface record; if no such interface is found, will throw 1366 // IllegalArgumentException 1367 TunnelInterfaceRecord tunnelInterfaceInfo = 1368 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); 1369 1370 try { 1371 // We can assume general validity of the IP address, since we get them as a 1372 // LinkAddress, which does some validation. 1373 mSrvConfig 1374 .getNetdInstance() 1375 .interfaceDelAddress( 1376 tunnelInterfaceInfo.mInterfaceName, 1377 localAddr.getAddress().getHostAddress(), 1378 localAddr.getPrefixLength()); 1379 } catch (RemoteException e) { 1380 throw e.rethrowFromSystemServer(); 1381 } 1382 } 1383 1384 /** 1385 * Delete a TunnelInterface that has been been allocated by and registered with the system 1386 * server 1387 */ 1388 @Override deleteTunnelInterface( int resourceId, String callingPackage)1389 public synchronized void deleteTunnelInterface( 1390 int resourceId, String callingPackage) throws RemoteException { 1391 enforceTunnelPermissions(callingPackage); 1392 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1393 releaseResource(userRecord.mTunnelInterfaceRecords, resourceId); 1394 } 1395 1396 @VisibleForTesting validateAlgorithms(IpSecConfig config)1397 void validateAlgorithms(IpSecConfig config) throws IllegalArgumentException { 1398 IpSecAlgorithm auth = config.getAuthentication(); 1399 IpSecAlgorithm crypt = config.getEncryption(); 1400 IpSecAlgorithm aead = config.getAuthenticatedEncryption(); 1401 1402 // Validate the algorithm set 1403 Preconditions.checkArgument( 1404 aead != null || crypt != null || auth != null, 1405 "No Encryption or Authentication algorithms specified"); 1406 Preconditions.checkArgument( 1407 auth == null || auth.isAuthentication(), 1408 "Unsupported algorithm for Authentication"); 1409 Preconditions.checkArgument( 1410 crypt == null || crypt.isEncryption(), "Unsupported algorithm for Encryption"); 1411 Preconditions.checkArgument( 1412 aead == null || aead.isAead(), 1413 "Unsupported algorithm for Authenticated Encryption"); 1414 Preconditions.checkArgument( 1415 aead == null || (auth == null && crypt == null), 1416 "Authenticated Encryption is mutually exclusive with other Authentication " 1417 + "or Encryption algorithms"); 1418 } 1419 1420 /** 1421 * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an 1422 * IllegalArgumentException if they are not. 1423 */ checkIpSecConfig(IpSecConfig config)1424 private void checkIpSecConfig(IpSecConfig config) { 1425 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1426 1427 switch (config.getEncapType()) { 1428 case IpSecTransform.ENCAP_NONE: 1429 break; 1430 case IpSecTransform.ENCAP_ESPINUDP: 1431 case IpSecTransform.ENCAP_ESPINUDP_NON_IKE: 1432 // Retrieve encap socket record; will throw IllegalArgumentException if not found 1433 userRecord.mEncapSocketRecords.getResourceOrThrow( 1434 config.getEncapSocketResourceId()); 1435 1436 int port = config.getEncapRemotePort(); 1437 if (port <= 0 || port > 0xFFFF) { 1438 throw new IllegalArgumentException("Invalid remote UDP port: " + port); 1439 } 1440 break; 1441 default: 1442 throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType()); 1443 } 1444 1445 validateAlgorithms(config); 1446 1447 // Retrieve SPI record; will throw IllegalArgumentException if not found 1448 SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId()); 1449 1450 // Check to ensure that SPI has not already been used. 1451 if (s.getOwnedByTransform()) { 1452 throw new IllegalStateException("SPI already in use; cannot be used in new Transforms"); 1453 } 1454 1455 // If no remote address is supplied, then use one from the SPI. 1456 if (TextUtils.isEmpty(config.getDestinationAddress())) { 1457 config.setDestinationAddress(s.getDestinationAddress()); 1458 } 1459 1460 // All remote addresses must match 1461 if (!config.getDestinationAddress().equals(s.getDestinationAddress())) { 1462 throw new IllegalArgumentException("Mismatched remote addresseses."); 1463 } 1464 1465 // This check is technically redundant due to the chain of custody between the SPI and 1466 // the IpSecConfig, but in the future if the dest is allowed to be set explicitly in 1467 // the transform, this will prevent us from messing up. 1468 checkInetAddress(config.getDestinationAddress()); 1469 1470 // Require a valid source address for all transforms. 1471 checkInetAddress(config.getSourceAddress()); 1472 1473 switch (config.getMode()) { 1474 case IpSecTransform.MODE_TRANSPORT: 1475 break; 1476 case IpSecTransform.MODE_TUNNEL: 1477 break; 1478 default: 1479 throw new IllegalArgumentException( 1480 "Invalid IpSecTransform.mode: " + config.getMode()); 1481 } 1482 } 1483 enforceTunnelPermissions(String callingPackage)1484 private void enforceTunnelPermissions(String callingPackage) { 1485 checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels"); 1486 switch (getAppOpsManager().noteOp( 1487 AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, 1488 Binder.getCallingUid(), callingPackage)) { 1489 case AppOpsManager.MODE_DEFAULT: 1490 mContext.enforceCallingOrSelfPermission( 1491 android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); 1492 break; 1493 case AppOpsManager.MODE_ALLOWED: 1494 return; 1495 default: 1496 throw new SecurityException("Request to ignore AppOps for non-legacy API"); 1497 } 1498 } 1499 createOrUpdateTransform( IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)1500 private void createOrUpdateTransform( 1501 IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord) 1502 throws RemoteException { 1503 1504 int encapType = c.getEncapType(), encapLocalPort = 0, encapRemotePort = 0; 1505 if (encapType != IpSecTransform.ENCAP_NONE) { 1506 encapLocalPort = socketRecord.getPort(); 1507 encapRemotePort = c.getEncapRemotePort(); 1508 } 1509 1510 IpSecAlgorithm auth = c.getAuthentication(); 1511 IpSecAlgorithm crypt = c.getEncryption(); 1512 IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption(); 1513 1514 String cryptName; 1515 if (crypt == null) { 1516 cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : ""; 1517 } else { 1518 cryptName = crypt.getName(); 1519 } 1520 1521 mSrvConfig 1522 .getNetdInstance() 1523 .ipSecAddSecurityAssociation( 1524 resourceId, 1525 c.getMode(), 1526 c.getSourceAddress(), 1527 c.getDestinationAddress(), 1528 (c.getNetwork() != null) ? c.getNetwork().netId : 0, 1529 spiRecord.getSpi(), 1530 c.getMarkValue(), 1531 c.getMarkMask(), 1532 (auth != null) ? auth.getName() : "", 1533 (auth != null) ? auth.getKey() : new byte[] {}, 1534 (auth != null) ? auth.getTruncationLengthBits() : 0, 1535 cryptName, 1536 (crypt != null) ? crypt.getKey() : new byte[] {}, 1537 (crypt != null) ? crypt.getTruncationLengthBits() : 0, 1538 (authCrypt != null) ? authCrypt.getName() : "", 1539 (authCrypt != null) ? authCrypt.getKey() : new byte[] {}, 1540 (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0, 1541 encapType, 1542 encapLocalPort, 1543 encapRemotePort); 1544 } 1545 1546 /** 1547 * Create a IPsec transform, which represents a single security association in the kernel. The 1548 * transform will be cached by the system server and must be freed when no longer needed. It is 1549 * possible to free one, deleting the SA from underneath sockets that are using it, which will 1550 * result in all of those sockets becoming unable to send or receive data. 1551 */ 1552 @Override createTransform( IpSecConfig c, IBinder binder, String callingPackage)1553 public synchronized IpSecTransformResponse createTransform( 1554 IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException { 1555 checkNotNull(c); 1556 if (c.getMode() == IpSecTransform.MODE_TUNNEL) { 1557 enforceTunnelPermissions(callingPackage); 1558 } 1559 checkIpSecConfig(c); 1560 checkNotNull(binder, "Null Binder passed to createTransform"); 1561 final int resourceId = mNextResourceId++; 1562 1563 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1564 List<RefcountedResource> dependencies = new ArrayList<>(); 1565 1566 if (!userRecord.mTransformQuotaTracker.isAvailable()) { 1567 return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); 1568 } 1569 1570 EncapSocketRecord socketRecord = null; 1571 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) { 1572 RefcountedResource<EncapSocketRecord> refcountedSocketRecord = 1573 userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow( 1574 c.getEncapSocketResourceId()); 1575 dependencies.add(refcountedSocketRecord); 1576 socketRecord = refcountedSocketRecord.getResource(); 1577 } 1578 1579 RefcountedResource<SpiRecord> refcountedSpiRecord = 1580 userRecord.mSpiRecords.getRefcountedResourceOrThrow(c.getSpiResourceId()); 1581 dependencies.add(refcountedSpiRecord); 1582 SpiRecord spiRecord = refcountedSpiRecord.getResource(); 1583 1584 createOrUpdateTransform(c, resourceId, spiRecord, socketRecord); 1585 1586 // SA was created successfully, time to construct a record and lock it away 1587 userRecord.mTransformRecords.put( 1588 resourceId, 1589 new RefcountedResource<TransformRecord>( 1590 new TransformRecord(resourceId, c, spiRecord, socketRecord), 1591 binder, 1592 dependencies.toArray(new RefcountedResource[dependencies.size()]))); 1593 return new IpSecTransformResponse(IpSecManager.Status.OK, resourceId); 1594 } 1595 1596 /** 1597 * Delete a transport mode transform that was previously allocated by + registered with the 1598 * system server. If this is called on an inactive (or non-existent) transform, it will not 1599 * return an error. It's safe to de-allocate transforms that may have already been deleted for 1600 * other reasons. 1601 */ 1602 @Override deleteTransform(int resourceId)1603 public synchronized void deleteTransform(int resourceId) throws RemoteException { 1604 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1605 releaseResource(userRecord.mTransformRecords, resourceId); 1606 } 1607 1608 /** 1609 * Apply an active transport mode transform to a socket, which will apply the IPsec security 1610 * association as a correspondent policy to the provided socket 1611 */ 1612 @Override applyTransportModeTransform( ParcelFileDescriptor socket, int direction, int resourceId)1613 public synchronized void applyTransportModeTransform( 1614 ParcelFileDescriptor socket, int direction, int resourceId) throws RemoteException { 1615 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1616 checkDirection(direction); 1617 // Get transform record; if no transform is found, will throw IllegalArgumentException 1618 TransformRecord info = userRecord.mTransformRecords.getResourceOrThrow(resourceId); 1619 1620 // TODO: make this a function. 1621 if (info.pid != getCallingPid() || info.uid != getCallingUid()) { 1622 throw new SecurityException("Only the owner of an IpSec Transform may apply it!"); 1623 } 1624 1625 // Get config and check that to-be-applied transform has the correct mode 1626 IpSecConfig c = info.getConfig(); 1627 Preconditions.checkArgument( 1628 c.getMode() == IpSecTransform.MODE_TRANSPORT, 1629 "Transform mode was not Transport mode; cannot be applied to a socket"); 1630 1631 mSrvConfig 1632 .getNetdInstance() 1633 .ipSecApplyTransportModeTransform( 1634 socket.getFileDescriptor(), 1635 resourceId, 1636 direction, 1637 c.getSourceAddress(), 1638 c.getDestinationAddress(), 1639 info.getSpiRecord().getSpi()); 1640 } 1641 1642 /** 1643 * Remove transport mode transforms from a socket, applying the default (empty) policy. This 1644 * ensures that NO IPsec policy is applied to the socket (would be the equivalent of applying a 1645 * policy that performs no IPsec). Today the resourceId parameter is passed but not used: 1646 * reserved for future improved input validation. 1647 */ 1648 @Override removeTransportModeTransforms(ParcelFileDescriptor socket)1649 public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket) 1650 throws RemoteException { 1651 mSrvConfig 1652 .getNetdInstance() 1653 .ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); 1654 } 1655 1656 /** 1657 * Apply an active tunnel mode transform to a TunnelInterface, which will apply the IPsec 1658 * security association as a correspondent policy to the provided interface 1659 */ 1660 @Override applyTunnelModeTransform( int tunnelResourceId, int direction, int transformResourceId, String callingPackage)1661 public synchronized void applyTunnelModeTransform( 1662 int tunnelResourceId, int direction, 1663 int transformResourceId, String callingPackage) throws RemoteException { 1664 enforceTunnelPermissions(callingPackage); 1665 checkDirection(direction); 1666 1667 UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); 1668 1669 // Get transform record; if no transform is found, will throw IllegalArgumentException 1670 TransformRecord transformInfo = 1671 userRecord.mTransformRecords.getResourceOrThrow(transformResourceId); 1672 1673 // Get tunnelInterface record; if no such interface is found, will throw 1674 // IllegalArgumentException 1675 TunnelInterfaceRecord tunnelInterfaceInfo = 1676 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId); 1677 1678 // Get config and check that to-be-applied transform has the correct mode 1679 IpSecConfig c = transformInfo.getConfig(); 1680 Preconditions.checkArgument( 1681 c.getMode() == IpSecTransform.MODE_TUNNEL, 1682 "Transform mode was not Tunnel mode; cannot be applied to a tunnel interface"); 1683 1684 EncapSocketRecord socketRecord = null; 1685 if (c.getEncapType() != IpSecTransform.ENCAP_NONE) { 1686 socketRecord = 1687 userRecord.mEncapSocketRecords.getResourceOrThrow(c.getEncapSocketResourceId()); 1688 } 1689 SpiRecord spiRecord = userRecord.mSpiRecords.getResourceOrThrow(c.getSpiResourceId()); 1690 1691 int mark = 1692 (direction == IpSecManager.DIRECTION_IN) 1693 ? tunnelInterfaceInfo.getIkey() 1694 : tunnelInterfaceInfo.getOkey(); 1695 1696 try { 1697 c.setMarkValue(mark); 1698 c.setMarkMask(0xffffffff); 1699 1700 if (direction == IpSecManager.DIRECTION_OUT) { 1701 // Set output mark via underlying network (output only) 1702 c.setNetwork(tunnelInterfaceInfo.getUnderlyingNetwork()); 1703 1704 // If outbound, also add SPI to the policy. 1705 for(String wildcardAddr : WILDCARD_ADDRESSES) { 1706 mSrvConfig 1707 .getNetdInstance() 1708 .ipSecUpdateSecurityPolicy( 1709 0, // Use 0 for reqId 1710 direction, 1711 wildcardAddr, 1712 wildcardAddr, 1713 transformInfo.getSpiRecord().getSpi(), 1714 mark, 1715 0xffffffff); 1716 } 1717 } 1718 1719 // Update SA with tunnel mark (ikey or okey based on direction) 1720 createOrUpdateTransform(c, transformResourceId, spiRecord, socketRecord); 1721 } catch (ServiceSpecificException e) { 1722 if (e.errorCode == EINVAL) { 1723 throw new IllegalArgumentException(e.toString()); 1724 } else { 1725 throw e; 1726 } 1727 } 1728 } 1729 1730 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1731 protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1732 mContext.enforceCallingOrSelfPermission(DUMP, TAG); 1733 1734 pw.println("IpSecService dump:"); 1735 pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead")); 1736 pw.println(); 1737 1738 pw.println("mUserResourceTracker:"); 1739 pw.println(mUserResourceTracker); 1740 } 1741 } 1742