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 { 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 */ 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 */ 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 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 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 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") 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") 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 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 320 ResourceTracker(int max) { 321 mMax = max; 322 mCurrent = 0; 323 } 324 325 boolean isAvailable() { 326 return (mCurrent < mMax); 327 } 328 329 void take() { 330 if (!isAvailable()) { 331 Log.wtf(TAG, "Too many resources allocated!"); 332 } 333 mCurrent++; 334 } 335 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 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 395 void removeSpiRecord(int resourceId) { 396 mSpiRecords.remove(resourceId); 397 } 398 399 void removeTransformRecord(int resourceId) { 400 mTransformRecords.remove(resourceId); 401 } 402 403 void removeTunnelInterfaceRecord(int resourceId) { 404 mTunnelInterfaceRecords.remove(resourceId); 405 } 406 407 void removeEncapSocketRecord(int resourceId) { 408 mEncapSocketRecords.remove(resourceId); 409 } 410 411 @Override 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 */ 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 */ 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 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 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 498 public abstract void invalidate() throws RemoteException; 499 500 /** Convenience method; retrieves the user resource record for the stored UID. */ 501 protected UserRecord getUserRecord() { 502 return mUserResourceTracker.getUserRecord(uid); 503 } 504 505 @Override 506 public abstract void freeUnderlyingResources() throws RemoteException; 507 508 /** Get the resource tracker for this resource */ 509 protected abstract ResourceTracker getResourceTracker(); 510 511 @Override 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 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 */ 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 */ 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 563 void put(int key, RefcountedResource<T> obj) { 564 checkNotNull(obj, "Null resources cannot be added"); 565 mArray.put(key, obj); 566 } 567 568 void remove(int key) { 569 mArray.remove(key); 570 } 571 572 @Override 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 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 598 public IpSecConfig getConfig() { 599 return mConfig; 600 } 601 602 public SpiRecord getSpiRecord() { 603 return mSpi; 604 } 605 606 public EncapSocketRecord getSocketRecord() { 607 return mSocket; 608 } 609 610 /** always guarded by IpSecService#this */ 611 @Override 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 632 public void invalidate() throws RemoteException { 633 getUserRecord().removeTransformRecord(mResourceId); 634 } 635 636 @Override 637 protected ResourceTracker getResourceTracker() { 638 return getUserRecord().mTransformQuotaTracker; 639 } 640 641 @Override 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 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 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 696 public int getSpi() { 697 return mSpi; 698 } 699 700 public String getDestinationAddress() { 701 return mDestinationAddress; 702 } 703 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 713 public boolean getOwnedByTransform() { 714 return mOwnedByTransform; 715 } 716 717 @Override 718 public void invalidate() throws RemoteException { 719 getUserRecord().removeSpiRecord(mResourceId); 720 } 721 722 @Override 723 protected ResourceTracker getResourceTracker() { 724 return getUserRecord().mSpiQuotaTracker; 725 } 726 727 @Override 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 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 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 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 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 847 public String getInterfaceName() { 848 return mInterfaceName; 849 } 850 851 public Network getUnderlyingNetwork() { 852 return mUnderlyingNetwork; 853 } 854 855 /** Returns the local, outer address for the tunnelInterface */ 856 public String getLocalAddress() { 857 return mLocalAddress; 858 } 859 860 /** Returns the remote, outer address for the tunnelInterface */ 861 public String getRemoteAddress() { 862 return mRemoteAddress; 863 } 864 865 public int getIkey() { 866 return mIkey; 867 } 868 869 public int getOkey() { 870 return mOkey; 871 } 872 873 @Override 874 protected ResourceTracker getResourceTracker() { 875 return getUserRecord().mTunnelQuotaTracker; 876 } 877 878 @Override 879 public void invalidate() { 880 getUserRecord().removeTunnelInterfaceRecord(mResourceId); 881 } 882 883 @Override 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 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 924 public void freeUnderlyingResources() { 925 Log.d(TAG, "Closing port " + mPort); 926 IoUtils.closeQuietly(mSocket); 927 mSocket = null; 928 929 getResourceTracker().give(); 930 } 931 932 public int getPort() { 933 return mPort; 934 } 935 936 public FileDescriptor getFileDescriptor() { 937 return mSocket; 938 } 939 940 @Override 941 protected ResourceTracker getResourceTracker() { 942 return getUserRecord().mSocketQuotaTracker; 943 } 944 945 @Override 946 public void invalidate() { 947 getUserRecord().removeEncapSocketRecord(mResourceId); 948 } 949 950 @Override 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 */ 969 private IpSecService(Context context) { 970 this(context, IpSecServiceConfiguration.GETSRVINSTANCE); 971 } 972 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 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 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 1004 public IpSecService( 1005 Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) { 1006 mContext = context; 1007 mSrvConfig = config; 1008 mUidFdTagger = uidFdTagger; 1009 } 1010 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 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 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 */ 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 */ 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 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 */ 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 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 */ 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 */ 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 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 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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