From b9df8b1fca70fa3bdd836b8ecabb252abaa5fcfe Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Wed, 21 Mar 2018 15:32:42 -0700 Subject: [PATCH 1/5] Add MANAGE_IPSEC_TUNNELS Permission Add a new MANAGE_IPSEC_TUNNELS permission and protect all IPsec Tunnel mode APIs with it. This permission is only granted to the system or through an AppOp. Bug: 66955045 Test: compilation Merged-In: I0f618373b500c493ef2211bece681f74652a1833 Change-Id: I0f618373b500c493ef2211bece681f74652a1833 (cherry picked from commit 00e77247ebe59b923fb7e257c80d2a2394c6b87d) --- core/java/android/net/IpSecManager.java | 6 ++++-- core/java/android/net/IpSecTransform.java | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index cc227713cb..d2c83e2b5e 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -691,6 +691,7 @@ public final class IpSecManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull LinkAddress address) throws IOException { try { mService.addAddressToTunnelInterface(mResourceId, address); @@ -708,6 +709,7 @@ public final class IpSecManager { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull LinkAddress address) throws IOException { try { mService.removeAddressFromTunnelInterface(mResourceId, address); @@ -801,7 +803,7 @@ public final class IpSecManager { */ @SystemApi @NonNull - @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) throws ResourceUnavailableException, IOException { @@ -826,7 +828,7 @@ public final class IpSecManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel, @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException { try { diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index cf58647bbb..099fe02fdd 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -282,7 +282,7 @@ public final class IpSecTransform implements AutoCloseable { */ @SystemApi @RequiresPermission(anyOf = { - android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD }) public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback, @@ -325,7 +325,7 @@ public final class IpSecTransform implements AutoCloseable { */ @SystemApi @RequiresPermission(anyOf = { - android.Manifest.permission.NETWORK_STACK, + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD }) public void stopNattKeepalive() { @@ -478,7 +478,7 @@ public final class IpSecTransform implements AutoCloseable { */ @SystemApi @NonNull - @RequiresPermission(android.Manifest.permission.NETWORK_STACK) + @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public IpSecTransform buildTunnelModeTransform( @NonNull InetAddress sourceAddress, @NonNull IpSecManager.SecurityParameterIndex spi) From cac8775b2a40d9179704db68cf50c25c4a5c903c Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Thu, 15 Mar 2018 18:06:06 -0700 Subject: [PATCH 2/5] Add AppOps Checks for MANAGE_IPSEC_TUNNELS Adds support for a new AppOp to permit services to use IpSec tunnel mode. The IpSecService now needs a context so change the service mode to a cached service rather than a static service. Bug: 66955045 Test: runtest frameworks-net Merged-In: I17a4a286225b432c3e15ea1587d946189931b4f4 Change-Id: I17a4a286225b432c3e15ea1587d946189931b4f4 (cherry picked from commit 65ef843176520902f6901b05369eaa0dabbe6516) --- core/java/android/net/IIpSecService.aidl | 20 ++++--- core/java/android/net/IpSecManager.java | 25 +++++--- core/java/android/net/IpSecTransform.java | 3 +- .../java/com/android/server/IpSecService.java | 59 ++++++++++++++----- 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl index 3a3ddcc483..d6774d47b4 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/core/java/android/net/IIpSecService.aidl @@ -45,25 +45,31 @@ interface IIpSecService in String localAddr, in String remoteAddr, in Network underlyingNetwork, - in IBinder binder); + in IBinder binder, + in String callingPackage); void addAddressToTunnelInterface( int tunnelResourceId, - in LinkAddress localAddr); + in LinkAddress localAddr, + in String callingPackage); void removeAddressFromTunnelInterface( int tunnelResourceId, - in LinkAddress localAddr); + in LinkAddress localAddr, + in String callingPackage); - void deleteTunnelInterface(int resourceId); + void deleteTunnelInterface(int resourceId, in String callingPackage); - IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder); + IpSecTransformResponse createTransform( + in IpSecConfig c, in IBinder binder, in String callingPackage); void deleteTransform(int transformId); - void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId); + void applyTransportModeTransform( + in ParcelFileDescriptor socket, int direction, int transformId); - void applyTunnelModeTransform(int tunnelResourceId, int direction, int transformResourceId); + void applyTunnelModeTransform( + int tunnelResourceId, int direction, int transformResourceId, in String callingPackage); void removeTransportModeTransforms(in ParcelFileDescriptor socket); } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index d2c83e2b5e..0d04fe5a91 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -140,6 +140,7 @@ public final class IpSecManager { } } + private final Context mContext; private final IIpSecService mService; /** @@ -667,6 +668,7 @@ public final class IpSecManager { */ @SystemApi public static final class IpSecTunnelInterface implements AutoCloseable { + private final String mOpPackageName; private final IIpSecService mService; private final InetAddress mRemoteAddress; private final InetAddress mLocalAddress; @@ -694,7 +696,8 @@ public final class IpSecManager { @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull LinkAddress address) throws IOException { try { - mService.addAddressToTunnelInterface(mResourceId, address); + mService.addAddressToTunnelInterface( + mResourceId, address, mOpPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -712,16 +715,18 @@ public final class IpSecManager { @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull LinkAddress address) throws IOException { try { - mService.removeAddressFromTunnelInterface(mResourceId, address); + mService.removeAddressFromTunnelInterface( + mResourceId, address, mOpPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - private IpSecTunnelInterface(@NonNull IIpSecService service, + private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service, @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) throws ResourceUnavailableException, IOException { + mOpPackageName = ctx.getOpPackageName(); mService = service; mLocalAddress = localAddress; mRemoteAddress = remoteAddress; @@ -733,7 +738,8 @@ public final class IpSecManager { localAddress.getHostAddress(), remoteAddress.getHostAddress(), underlyingNetwork, - new Binder()); + new Binder(), + mOpPackageName); switch (result.status) { case Status.OK: break; @@ -762,7 +768,7 @@ public final class IpSecManager { @Override public void close() { try { - mService.deleteTunnelInterface(mResourceId); + mService.deleteTunnelInterface(mResourceId, mOpPackageName); mResourceId = INVALID_RESOURCE_ID; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -807,7 +813,8 @@ public final class IpSecManager { public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) throws ResourceUnavailableException, IOException { - return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork); + return new IpSecTunnelInterface( + mContext, mService, localAddress, remoteAddress, underlyingNetwork); } /** @@ -833,7 +840,8 @@ public final class IpSecManager { @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException { try { mService.applyTunnelModeTransform( - tunnel.getResourceId(), direction, transform.getResourceId()); + tunnel.getResourceId(), direction, + transform.getResourceId(), mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -845,7 +853,8 @@ public final class IpSecManager { * @param context the application context for this manager * @hide */ - public IpSecManager(IIpSecService service) { + public IpSecManager(Context ctx, IIpSecService service) { + mContext = ctx; mService = checkNotNull(service, "missing service"); } } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 099fe02fdd..fb5f46c117 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -130,7 +130,8 @@ public final class IpSecTransform implements AutoCloseable { synchronized (this) { try { IIpSecService svc = getIpSecService(); - IpSecTransformResponse result = svc.createTransform(mConfig, new Binder()); + IpSecTransformResponse result = svc.createTransform( + mConfig, new Binder(), mContext.getOpPackageName()); int status = result.status; checkResultStatus(status); mResourceId = result.resourceId; diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 35bde8e18d..9709885887 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -24,6 +24,8 @@ import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; import static com.android.internal.util.Preconditions.checkNotNull; +import android.annotation.NonNull; +import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; import android.net.IIpSecService; @@ -42,6 +44,7 @@ import android.net.NetworkUtils; import android.net.TrafficStats; import android.net.util.NetdService; import android.os.Binder; +import android.os.DeadSystemException; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -973,6 +976,13 @@ public class IpSecService extends IIpSecService.Stub { return service; } + @NonNull + private AppOpsManager getAppOpsManager() { + AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + if(appOps == null) throw new RuntimeException("System Server couldn't get AppOps"); + return appOps; + } + /** @hide */ @VisibleForTesting public IpSecService(Context context, IpSecServiceConfiguration config) { @@ -1239,7 +1249,9 @@ public class IpSecService extends IIpSecService.Stub { */ @Override public synchronized IpSecTunnelInterfaceResponse createTunnelInterface( - String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder) { + String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder, + String callingPackage) { + enforceTunnelPermissions(callingPackage); checkNotNull(binder, "Null Binder passed to createTunnelInterface"); checkNotNull(underlyingNetwork, "No underlying network was specified"); checkInetAddress(localAddr); @@ -1319,8 +1331,8 @@ public class IpSecService extends IIpSecService.Stub { */ @Override public synchronized void addAddressToTunnelInterface( - int tunnelResourceId, LinkAddress localAddr) { - enforceNetworkStackPermission(); + int tunnelResourceId, LinkAddress localAddr, String callingPackage) { + enforceTunnelPermissions(callingPackage); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); // Get tunnelInterface record; if no such interface is found, will throw @@ -1351,10 +1363,10 @@ public class IpSecService extends IIpSecService.Stub { */ @Override public synchronized void removeAddressFromTunnelInterface( - int tunnelResourceId, LinkAddress localAddr) { - enforceNetworkStackPermission(); - UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); + int tunnelResourceId, LinkAddress localAddr, String callingPackage) { + enforceTunnelPermissions(callingPackage); + UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); // Get tunnelInterface record; if no such interface is found, will throw // IllegalArgumentException TunnelInterfaceRecord tunnelInterfaceInfo = @@ -1382,7 +1394,9 @@ public class IpSecService extends IIpSecService.Stub { * server */ @Override - public synchronized void deleteTunnelInterface(int resourceId) throws RemoteException { + public synchronized void deleteTunnelInterface( + int resourceId, String callingPackage) throws RemoteException { + enforceTunnelPermissions(callingPackage); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); releaseResource(userRecord.mTunnelInterfaceRecords, resourceId); } @@ -1468,7 +1482,6 @@ public class IpSecService extends IIpSecService.Stub { case IpSecTransform.MODE_TRANSPORT: break; case IpSecTransform.MODE_TUNNEL: - enforceNetworkStackPermission(); break; default: throw new IllegalArgumentException( @@ -1476,9 +1489,20 @@ public class IpSecService extends IIpSecService.Stub { } } - private void enforceNetworkStackPermission() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK, - "IpSecService"); + private void enforceTunnelPermissions(String callingPackage) { + checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels"); + switch (getAppOpsManager().noteOp( + AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, + Binder.getCallingUid(), callingPackage)) { + case AppOpsManager.MODE_DEFAULT: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); + break; + case AppOpsManager.MODE_ALLOWED: + return; + default: + throw new SecurityException("Request to ignore AppOps for non-legacy API"); + } } private void createOrUpdateTransform( @@ -1534,8 +1558,12 @@ public class IpSecService extends IIpSecService.Stub { * result in all of those sockets becoming unable to send or receive data. */ @Override - public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder) - throws RemoteException { + public synchronized IpSecTransformResponse createTransform( + IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException { + checkNotNull(c); + if (c.getMode() == IpSecTransform.MODE_TUNNEL) { + enforceTunnelPermissions(callingPackage); + } checkIpSecConfig(c); checkNotNull(binder, "Null Binder passed to createTransform"); final int resourceId = mNextResourceId++; @@ -1656,8 +1684,9 @@ public class IpSecService extends IIpSecService.Stub { */ @Override public synchronized void applyTunnelModeTransform( - int tunnelResourceId, int direction, int transformResourceId) throws RemoteException { - enforceNetworkStackPermission(); + int tunnelResourceId, int direction, + int transformResourceId, String callingPackage) throws RemoteException { + enforceTunnelPermissions(callingPackage); checkDirection(direction); UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); From b25e678b4b5c0c2cad47ff257f08a0c433457ff2 Mon Sep 17 00:00:00 2001 From: Benedict Wong Date: Tue, 3 Apr 2018 20:30:54 -0700 Subject: [PATCH 3/5] Update IpSecManager to use InetAddress and prefixLen LinkAddress constructors are currently @hide; this change updates IpSecManager to use InetAddress and prefixLen, and then construct a LinkAddress internally. LinkAddress is used over the binder interface to IpSecService to ensure validity. Bug: 77528639 Test: CTS, Java unit tests ran on walleye Merged-In: I19e124adef6d9f4992d8293db3190bcf74c95848 Change-Id: I19e124adef6d9f4992d8293db3190bcf74c95848 (cherry picked from commit d39837f7e207fba1b79279f5a90178baacf5ffe6) --- core/java/android/net/IpSecManager.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 0d04fe5a91..87323755a9 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -690,14 +690,15 @@ public final class IpSecManager { * tunneled traffic. * * @param address the local address for traffic inside the tunnel + * @param prefixLen length of the InetAddress prefix * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) - public void addAddress(@NonNull LinkAddress address) throws IOException { + public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException { try { mService.addAddressToTunnelInterface( - mResourceId, address, mOpPackageName); + mResourceId, new LinkAddress(address, prefixLen), mOpPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -709,14 +710,15 @@ public final class IpSecManager { *

Remove an address which was previously added to the IpSecTunnelInterface * * @param address to be removed + * @param prefixLen length of the InetAddress prefix * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) - public void removeAddress(@NonNull LinkAddress address) throws IOException { + public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException { try { mService.removeAddressFromTunnelInterface( - mResourceId, address, mOpPackageName); + mResourceId, new LinkAddress(address, prefixLen), mOpPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } From d21e909fd3cc633c4cd2f09d341a04e3a787972a Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Tue, 3 Apr 2018 16:13:19 -0700 Subject: [PATCH 4/5] Rework Exception Handling for IpSecManager In order to properly support EOPNOTSUPP this CL applies a consistent approach to handling Exceptions. Hereafter, all exceptions that aren't of a special method-specific type (such as SpiUnavailableException) will all be returned to the calling process unchanged. At the API call site, the ServiceSpecificException, which is really an Errno, will be inspected and either converted to an unchecked exception for types we know, or it will be converted to an IOException in cases where that method can return a checked exception. In cases where we do not expect an errno, we will simply throw a generic RuntimeException. This means all API calls will now properly throw UnsupportedOperationException and may be CTS tested accordingly. Bug: 72420898 Test: runtest frameworks-net Merged-In: I4a00e221618896223fcdb4b4279fb14cd14e34d8 Change-Id: I4a00e221618896223fcdb4b4279fb14cd14e34d8 (cherry picked from commit beed0b61b7f607cd14e43b56fb1cd2f80b53d4f1) --- core/java/android/net/IpSecManager.java | 147 ++++++++++++++++-- core/java/android/net/IpSecTransform.java | 16 ++ .../java/com/android/server/IpSecService.java | 71 +++------ 3 files changed, 173 insertions(+), 61 deletions(-) diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 87323755a9..1145d5bd4d 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -27,6 +27,9 @@ import android.content.Context; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.system.ErrnoException; +import android.system.OsConstants; import android.util.AndroidException; import android.util.Log; @@ -173,11 +176,16 @@ public final class IpSecManager { public void close() { try { mService.releaseSecurityParameterIndex(mResourceId); - mResourceId = INVALID_RESOURCE_ID; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); + } catch (Exception e) { + // On close we swallow all random exceptions since failure to close is not + // actionable by the user. + Log.e(TAG, "Failed to close " + this + ", Exception=" + e); + } finally { + mResourceId = INVALID_RESOURCE_ID; + mCloseGuard.close(); } - mCloseGuard.close(); } /** Check that the SPI was closed properly. */ @@ -228,7 +236,6 @@ public final class IpSecManager { throw new RuntimeException( "Invalid Resource ID returned by IpSecService: " + status); } - } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -240,6 +247,17 @@ public final class IpSecManager { public int getResourceId() { return mResourceId; } + + @Override + public String toString() { + return new StringBuilder() + .append("SecurityParameterIndex{spi=") + .append(mSpi) + .append(",resourceId=") + .append(mResourceId) + .append("}") + .toString(); + } } /** @@ -262,7 +280,11 @@ public final class IpSecManager { mService, destinationAddress, IpSecManager.INVALID_SECURITY_PARAMETER_INDEX); + } catch (ServiceSpecificException e) { + throw rethrowUncheckedExceptionFromServiceSpecificException(e); } catch (SpiUnavailableException unlikely) { + // Because this function allocates a totally random SPI, it really shouldn't ever + // fail to allocate an SPI; we simply need this because the exception is checked. throw new ResourceUnavailableException("No SPIs available"); } } @@ -275,8 +297,8 @@ public final class IpSecManager { * * @param destinationAddress the destination address for traffic bearing the requested SPI. * For inbound traffic, the destination should be an address currently assigned on-device. - * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is - * reserved and may not be used. See RFC 4303 Section 2.1. + * @param requestedSpi the requested SPI. The range 1-255 is reserved and may not be used. See + * RFC 4303 Section 2.1. * @return the reserved SecurityParameterIndex * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are * currently allocated for this user @@ -290,7 +312,11 @@ public final class IpSecManager { if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) { throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI"); } - return new SecurityParameterIndex(mService, destinationAddress, requestedSpi); + try { + return new SecurityParameterIndex(mService, destinationAddress, requestedSpi); + } catch (ServiceSpecificException e) { + throw rethrowUncheckedExceptionFromServiceSpecificException(e); + } } /** @@ -425,6 +451,8 @@ public final class IpSecManager { // constructor takes control and closes the user's FD when we exit the method. try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { mService.applyTransportModeTransform(pfd, direction, transform.getResourceId()); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -483,6 +511,8 @@ public final class IpSecManager { public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException { try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) { mService.removeTransportModeTransforms(pfd); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -576,6 +606,13 @@ public final class IpSecManager { mResourceId = INVALID_RESOURCE_ID; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); + } catch (Exception e) { + // On close we swallow all random exceptions since failure to close is not + // actionable by the user. + Log.e(TAG, "Failed to close " + this + ", Exception=" + e); + } finally { + mResourceId = INVALID_RESOURCE_ID; + mCloseGuard.close(); } try { @@ -584,7 +621,6 @@ public final class IpSecManager { Log.e(TAG, "Failed to close UDP Encapsulation Socket with Port= " + mPort); throw e; } - mCloseGuard.close(); } /** Check that the socket was closed properly. */ @@ -601,6 +637,17 @@ public final class IpSecManager { public int getResourceId() { return mResourceId; } + + @Override + public String toString() { + return new StringBuilder() + .append("UdpEncapsulationSocket{port=") + .append(mPort) + .append(",resourceId=") + .append(mResourceId) + .append("}") + .toString(); + } }; /** @@ -628,7 +675,11 @@ public final class IpSecManager { if (port == 0) { throw new IllegalArgumentException("Specified port must be a valid port number!"); } - return new UdpEncapsulationSocket(mService, port); + try { + return new UdpEncapsulationSocket(mService, port); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); + } } /** @@ -651,7 +702,11 @@ public final class IpSecManager { @NonNull public UdpEncapsulationSocket openUdpEncapsulationSocket() throws IOException, ResourceUnavailableException { - return new UdpEncapsulationSocket(mService, 0); + try { + return new UdpEncapsulationSocket(mService, 0); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); + } } /** @@ -699,6 +754,8 @@ public final class IpSecManager { try { mService.addAddressToTunnelInterface( mResourceId, new LinkAddress(address, prefixLen), mOpPackageName); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -719,6 +776,8 @@ public final class IpSecManager { try { mService.removeAddressFromTunnelInterface( mResourceId, new LinkAddress(address, prefixLen), mOpPackageName); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -771,11 +830,16 @@ public final class IpSecManager { public void close() { try { mService.deleteTunnelInterface(mResourceId, mOpPackageName); - mResourceId = INVALID_RESOURCE_ID; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); + } catch (Exception e) { + // On close we swallow all random exceptions since failure to close is not + // actionable by the user. + Log.e(TAG, "Failed to close " + this + ", Exception=" + e); + } finally { + mResourceId = INVALID_RESOURCE_ID; + mCloseGuard.close(); } - mCloseGuard.close(); } /** Check that the Interface was closed properly. */ @@ -792,6 +856,17 @@ public final class IpSecManager { public int getResourceId() { return mResourceId; } + + @Override + public String toString() { + return new StringBuilder() + .append("IpSecTunnelInterface{ifname=") + .append(mInterfaceName) + .append(",resourceId=") + .append(mResourceId) + .append("}") + .toString(); + } } /** @@ -815,8 +890,12 @@ public final class IpSecManager { public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork) throws ResourceUnavailableException, IOException { - return new IpSecTunnelInterface( - mContext, mService, localAddress, remoteAddress, underlyingNetwork); + try { + return new IpSecTunnelInterface( + mContext, mService, localAddress, remoteAddress, underlyingNetwork); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); + } } /** @@ -844,6 +923,8 @@ public final class IpSecManager { mService.applyTunnelModeTransform( tunnel.getResourceId(), direction, transform.getResourceId(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw rethrowCheckedExceptionFromServiceSpecificException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -859,4 +940,44 @@ public final class IpSecManager { mContext = ctx; mService = checkNotNull(service, "missing service"); } + + private static void maybeHandleServiceSpecificException(ServiceSpecificException sse) { + // OsConstants are late binding, so switch statements can't be used. + if (sse.errorCode == OsConstants.EINVAL) { + throw new IllegalArgumentException(sse); + } else if (sse.errorCode == OsConstants.EAGAIN) { + throw new IllegalStateException(sse); + } else if (sse.errorCode == OsConstants.EOPNOTSUPP) { + throw new UnsupportedOperationException(sse); + } + } + + /** + * Convert an Errno SSE to the correct Unchecked exception type. + * + * This method never actually returns. + */ + // package + static RuntimeException + rethrowUncheckedExceptionFromServiceSpecificException(ServiceSpecificException sse) { + maybeHandleServiceSpecificException(sse); + throw new RuntimeException(sse); + } + + /** + * Convert an Errno SSE to the correct Checked or Unchecked exception type. + * + * This method may throw IOException, or it may throw an unchecked exception; it will never + * actually return. + */ + // package + static IOException rethrowCheckedExceptionFromServiceSpecificException( + ServiceSpecificException sse) throws IOException { + // First see if this is an unchecked exception of a type we know. + // If so, then we prefer the unchecked (specific) type of exception. + maybeHandleServiceSpecificException(sse); + // If not, then all we can do is provide the SSE in the form of an IOException. + throw new ErrnoException( + "IpSec encountered errno=" + sse.errorCode, sse.errorCode).rethrowAsIOException(); + } } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index fb5f46c117..23c8aa368d 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -137,6 +138,8 @@ public final class IpSecTransform implements AutoCloseable { mResourceId = result.resourceId; Log.d(TAG, "Added Transform with Id " + mResourceId); mCloseGuard.open("build"); + } catch (ServiceSpecificException e) { + throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -181,6 +184,10 @@ public final class IpSecTransform implements AutoCloseable { stopNattKeepalive(); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); + } catch (Exception e) { + // On close we swallow all random exceptions since failure to close is not + // actionable by the user. + Log.e(TAG, "Failed to close " + this + ", Exception=" + e); } finally { mResourceId = INVALID_RESOURCE_ID; mCloseGuard.close(); @@ -507,4 +514,13 @@ public final class IpSecTransform implements AutoCloseable { mConfig = new IpSecConfig(); } } + + @Override + public String toString() { + return new StringBuilder() + .append("IpSecTransform{resourceId=") + .append(mResourceId) + .append("}") + .toString(); + } } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 9709885887..60f1877d37 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1100,9 +1100,11 @@ public class IpSecService extends IIpSecService.Stub { new RefcountedResource( new SpiRecord(resourceId, "", destinationAddress, spi), binder)); } catch (ServiceSpecificException e) { - // TODO: Add appropriate checks when other ServiceSpecificException types are supported - return new IpSecSpiResponse( - IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi); + if (e.errorCode == OsConstants.ENOENT) { + return new IpSecSpiResponse( + IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi); + } + throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1114,7 +1116,6 @@ public class IpSecService extends IIpSecService.Stub { */ private void releaseResource(RefcountedResourceArray resArray, int resourceId) throws RemoteException { - resArray.getRefcountedResourceOrThrow(resourceId).userRelease(); } @@ -1314,15 +1315,12 @@ public class IpSecService extends IIpSecService.Stub { releaseNetId(ikey); releaseNetId(okey); throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception + } catch (Throwable t) { + // Release keys if we got an error. + releaseNetId(ikey); + releaseNetId(okey); + throw t; } - - // If we make it to here, then something has gone wrong and we couldn't create a VTI. - // Release the keys that we reserved, and return an error status. - releaseNetId(ikey); - releaseNetId(okey); - return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); } /** @@ -1351,9 +1349,6 @@ public class IpSecService extends IIpSecService.Stub { localAddr.getPrefixLength()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw. - throw new IllegalArgumentException(e); } } @@ -1383,9 +1378,6 @@ public class IpSecService extends IIpSecService.Stub { localAddr.getPrefixLength()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw. - throw new IllegalArgumentException(e); } } @@ -1589,12 +1581,7 @@ public class IpSecService extends IIpSecService.Stub { dependencies.add(refcountedSpiRecord); SpiRecord spiRecord = refcountedSpiRecord.getResource(); - try { - createOrUpdateTransform(c, resourceId, spiRecord, socketRecord); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); - } + createOrUpdateTransform(c, resourceId, spiRecord, socketRecord); // SA was created successfully, time to construct a record and lock it away userRecord.mTransformRecords.put( @@ -1641,23 +1628,15 @@ public class IpSecService extends IIpSecService.Stub { c.getMode() == IpSecTransform.MODE_TRANSPORT, "Transform mode was not Transport mode; cannot be applied to a socket"); - try { - mSrvConfig - .getNetdInstance() - .ipSecApplyTransportModeTransform( - socket.getFileDescriptor(), - resourceId, - direction, - c.getSourceAddress(), - c.getDestinationAddress(), - info.getSpiRecord().getSpi()); - } catch (ServiceSpecificException e) { - if (e.errorCode == EINVAL) { - throw new IllegalArgumentException(e.toString()); - } else { - throw e; - } - } + mSrvConfig + .getNetdInstance() + .ipSecApplyTransportModeTransform( + socket.getFileDescriptor(), + resourceId, + direction, + c.getSourceAddress(), + c.getDestinationAddress(), + info.getSpiRecord().getSpi()); } /** @@ -1669,13 +1648,9 @@ public class IpSecService extends IIpSecService.Stub { @Override public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket) throws RemoteException { - try { - mSrvConfig - .getNetdInstance() - .ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); - } catch (ServiceSpecificException e) { - // FIXME: get the error code and throw is at an IOException from Errno Exception - } + mSrvConfig + .getNetdInstance() + .ipSecRemoveTransportModeTransform(socket.getFileDescriptor()); } /** From 147f7386dad7b5a12b6b4ff8f53e3e8f252f501d Mon Sep 17 00:00:00 2001 From: Nathan Harold Date: Tue, 15 May 2018 19:18:38 -0700 Subject: [PATCH 5/5] Disable the AppOp Restriction for IpSec Tunnels This CL temporarily removes the AppOp restriction that disallows creation of IpSec tunnels due to the lack of the appropriate AppOp in AOSP/master. When the relevant framework merges out to master, this CL should be reverted. Bug: none Test: compilation Change-Id: Ic06c193f85f6bcdd0ead4238825c1add78703cde --- .../java/com/android/server/IpSecService.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 60f1877d37..744ed25f16 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1481,19 +1481,23 @@ public class IpSecService extends IIpSecService.Stub { } } + private static final String TUNNEL_OP = "STOPSHIP"; // = AppOpsManager.OP_MANAGE_IPSEC_TUNNELS; + private void enforceTunnelPermissions(String callingPackage) { checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels"); - switch (getAppOpsManager().noteOp( - AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, - Binder.getCallingUid(), callingPackage)) { - case AppOpsManager.MODE_DEFAULT: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); - break; - case AppOpsManager.MODE_ALLOWED: - return; - default: - throw new SecurityException("Request to ignore AppOps for non-legacy API"); + if (false) { // STOPSHIP if this line is present + switch (getAppOpsManager().noteOp( + TUNNEL_OP, + Binder.getCallingUid(), callingPackage)) { + case AppOpsManager.MODE_DEFAULT: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); + break; + case AppOpsManager.MODE_ALLOWED: + return; + default: + throw new SecurityException("Request to ignore AppOps for non-legacy API"); + } } }