diff options
author | serya@chromium.org <serya@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-08 09:17:41 +0000 |
---|---|---|
committer | serya@chromium.org <serya@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-08 09:19:27 +0000 |
commit | fe928be94514d6331264a36344316ff0abb8bfa7 (patch) | |
tree | 5a3f2d3f8dc18aa762c7bbdcb7127b4eb0128416 | |
parent | 236ccaa12431f101db8fc18d632352087e4eead9 (diff) | |
download | chromium_src-fe928be94514d6331264a36344316ff0abb8bfa7.zip chromium_src-fe928be94514d6331264a36344316ff0abb8bfa7.tar.gz chromium_src-fe928be94514d6331264a36344316ff0abb8bfa7.tar.bz2 |
Supports DevTools socket access authentication based on Android permissions.
BUG=399567
Review URL: https://codereview.chromium.org/382143005
Cr-Commit-Position: refs/heads/master@{#288273}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288273 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java | 33 | ||||
-rw-r--r-- | chrome/browser/android/dev_tools_server.cc | 47 | ||||
-rw-r--r-- | chrome/browser/android/dev_tools_server.h | 5 | ||||
-rw-r--r-- | content/browser/android/devtools_auth.cc | 17 | ||||
-rw-r--r-- | content/public/browser/android/devtools_auth.h | 10 | ||||
-rw-r--r-- | net/socket/unix_domain_client_socket_posix_unittest.cc | 10 | ||||
-rw-r--r-- | net/socket/unix_domain_listen_socket_posix.cc | 7 | ||||
-rw-r--r-- | net/socket/unix_domain_listen_socket_posix_unittest.cc | 2 | ||||
-rw-r--r-- | net/socket/unix_domain_server_socket_posix.cc | 20 | ||||
-rw-r--r-- | net/socket/unix_domain_server_socket_posix.h | 26 | ||||
-rw-r--r-- | net/socket/unix_domain_server_socket_posix_unittest.cc | 10 | ||||
-rw-r--r-- | remoting/host/gnubby_auth_handler_posix.cc | 6 |
12 files changed, 123 insertions, 70 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java b/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java index 7ed33f9..5f84994 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/DevToolsServer.java @@ -4,13 +4,29 @@ package org.chromium.chrome.browser; +import android.content.Context; +import android.content.pm.PackageManager; + +import org.chromium.base.CalledByNative; + /** * Controller for Remote Web Debugging (Developer Tools). */ public class DevToolsServer { + private static final String DEBUG_PERMISSION_SIFFIX = ".permission.DEBUG"; private long mNativeDevToolsServer = 0; + // Defines what processes may access to the socket. + public enum Security { + // Use content::CanUserConnectToDevTools to authorize access to the socket. + DEFAULT, + + // In addition to default authorization allows access to an app with android permission + // named chromeAppPackageName + DEBUG_PERMISSION_SIFFIX. + ALLOW_DEBUG_PERMISSION, + } + public DevToolsServer(String socketNamePrefix) { mNativeDevToolsServer = nativeInitRemoteDebugging(socketNamePrefix); } @@ -24,12 +40,25 @@ public class DevToolsServer { return nativeIsRemoteDebuggingEnabled(mNativeDevToolsServer); } + public void setRemoteDebuggingEnabled(boolean enabled, Security security) { + boolean allowDebugPermission = security == Security.ALLOW_DEBUG_PERMISSION; + nativeSetRemoteDebuggingEnabled(mNativeDevToolsServer, enabled, allowDebugPermission); + } + public void setRemoteDebuggingEnabled(boolean enabled) { - nativeSetRemoteDebuggingEnabled(mNativeDevToolsServer, enabled); + setRemoteDebuggingEnabled(enabled, Security.DEFAULT); } private native long nativeInitRemoteDebugging(String socketNamePrefix); private native void nativeDestroyRemoteDebugging(long devToolsServer); private native boolean nativeIsRemoteDebuggingEnabled(long devToolsServer); - private native void nativeSetRemoteDebuggingEnabled(long devToolsServer, boolean enabled); + private native void nativeSetRemoteDebuggingEnabled( + long devToolsServer, boolean enabled, boolean allowDebugPermission); + + @CalledByNative + private static boolean checkDebugPermission(Context context, int pid, int uid) { + String debugPermissionName = context.getPackageName() + DEBUG_PERMISSION_SIFFIX; + return context.checkPermission(debugPermissionName, pid, uid) + == PackageManager.PERMISSION_GRANTED; + } } diff --git a/chrome/browser/android/dev_tools_server.cc b/chrome/browser/android/dev_tools_server.cc index acdb6b5..3b8b3ce 100644 --- a/chrome/browser/android/dev_tools_server.cc +++ b/chrome/browser/android/dev_tools_server.cc @@ -63,7 +63,6 @@ const char kDevToolsChannelNameFormat[] = "%s_devtools_remote"; const char kFrontEndURL[] = "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html"; -const char kDefaultSocketNamePrefix[] = "chrome"; const char kTetheringSocketName[] = "chrome_devtools_tethering_%d_%d"; const char kTargetTypePage[] = "page"; @@ -77,6 +76,15 @@ static GURL GetFaviconURLForContents(WebContents* web_contents) { return GURL(); } +bool AuthorizeSocketAccessWithDebugPermission( + const net::UnixDomainServerSocket::Credentials& credentials) { + JNIEnv* env = base::android::AttachCurrentThread(); + return Java_DevToolsServer_checkDebugPermission( + env, base::android::GetApplicationContext(), + credentials.process_id, credentials.user_id) || + content::CanUserConnectToDevTools(credentials); +} + class TargetBase : public content::DevToolsTarget { public: // content::DevToolsTarget implementation: @@ -268,8 +276,10 @@ class NonTabTarget : public TargetBase { // instance of this gets created each time devtools is enabled. class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate { public: - DevToolsServerDelegate() - : last_tethering_socket_(0) { + explicit DevToolsServerDelegate( + const net::UnixDomainServerSocket::AuthCallback& auth_callback) + : last_tethering_socket_(0), + auth_callback_(auth_callback) { } virtual std::string GetDiscoveryPageHTML() OVERRIDE { @@ -371,7 +381,7 @@ class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate { *name, "", delegate, - base::Bind(&content::CanUserConnectToDevTools)) + auth_callback_) .PassAs<net::StreamListenSocket>(); } @@ -385,24 +395,13 @@ class DevToolsServerDelegate : public content::DevToolsHttpHandlerDelegate { } int last_tethering_socket_; + const net::UnixDomainServerSocket::AuthCallback auth_callback_; DISALLOW_COPY_AND_ASSIGN(DevToolsServerDelegate); }; } // namespace -DevToolsServer::DevToolsServer() - : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat, - kDefaultSocketNamePrefix)), - protocol_handler_(NULL) { - // Override the default socket name if one is specified on the command line. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kRemoteDebuggingSocketName)) { - socket_name_ = command_line.GetSwitchValueASCII( - switches::kRemoteDebuggingSocketName); - } -} - DevToolsServer::DevToolsServer(const std::string& socket_name_prefix) : socket_name_(base::StringPrintf(kDevToolsChannelNameFormat, socket_name_prefix.c_str())), @@ -419,17 +418,22 @@ DevToolsServer::~DevToolsServer() { Stop(); } -void DevToolsServer::Start() { +void DevToolsServer::Start(bool allow_debug_permission) { if (protocol_handler_) return; + net::UnixDomainServerSocket::AuthCallback auth_callback = + allow_debug_permission ? + base::Bind(&AuthorizeSocketAccessWithDebugPermission) : + base::Bind(&content::CanUserConnectToDevTools); + protocol_handler_ = content::DevToolsHttpHandler::Start( new net::deprecated::UnixDomainListenSocketWithAbstractNamespaceFactory( socket_name_, base::StringPrintf("%s_%d", socket_name_.c_str(), getpid()), - base::Bind(&content::CanUserConnectToDevTools)), + auth_callback), base::StringPrintf(kFrontEndURL, content::GetWebKitRevision().c_str()), - new DevToolsServerDelegate(), + new DevToolsServerDelegate(auth_callback), base::FilePath()); } @@ -471,10 +475,11 @@ static jboolean IsRemoteDebuggingEnabled(JNIEnv* env, static void SetRemoteDebuggingEnabled(JNIEnv* env, jobject obj, jlong server, - jboolean enabled) { + jboolean enabled, + jboolean allow_debug_permission) { DevToolsServer* devtools_server = reinterpret_cast<DevToolsServer*>(server); if (enabled) { - devtools_server->Start(); + devtools_server->Start(allow_debug_permission); } else { devtools_server->Stop(); } diff --git a/chrome/browser/android/dev_tools_server.h b/chrome/browser/android/dev_tools_server.h index 46a0870..67f5688 100644 --- a/chrome/browser/android/dev_tools_server.h +++ b/chrome/browser/android/dev_tools_server.h @@ -16,14 +16,11 @@ class DevToolsHttpHandler; // This class controls Developer Tools remote debugging server. class DevToolsServer { public: - // TODO(mnaganov): Remove the prefixless constructor after having its - // usage removed in the Chrome for Android code. - DevToolsServer(); explicit DevToolsServer(const std::string& socket_name_prefix); ~DevToolsServer(); // Opens linux abstract socket to be ready for remote debugging. - void Start(); + void Start(bool allow_debug_permission); // Closes debugging socket, stops debugging. void Stop(); diff --git a/content/browser/android/devtools_auth.cc b/content/browser/android/devtools_auth.cc index 777a01e..46314c4 100644 --- a/content/browser/android/devtools_auth.cc +++ b/content/browser/android/devtools_auth.cc @@ -4,23 +4,28 @@ #include "content/public/browser/android/devtools_auth.h" -#include <unistd.h> +#include <pwd.h> #include <sys/types.h> +#include <unistd.h> #include "base/logging.h" namespace content { -bool CanUserConnectToDevTools(uid_t uid, gid_t gid) { - struct passwd* creds = getpwuid(uid); +bool CanUserConnectToDevTools( + const net::UnixDomainServerSocket::Credentials& credentials) { + struct passwd* creds = getpwuid(credentials.user_id); if (!creds || !creds->pw_name) { - LOG(WARNING) << "DevTools: can't obtain creds for uid " << uid; + LOG(WARNING) << "DevTools: can't obtain creds for uid " + << credentials.user_id; return false; } - if (gid == uid && + if (credentials.group_id == credentials.user_id && (strcmp("root", creds->pw_name) == 0 || // For rooted devices strcmp("shell", creds->pw_name) == 0 || // For non-rooted devices - uid == getuid())) { // From processes signed with the same key + + // From processes signed with the same key + credentials.user_id == getuid())) { return true; } LOG(WARNING) << "DevTools: connection attempt from " << creds->pw_name; diff --git a/content/public/browser/android/devtools_auth.h b/content/public/browser/android/devtools_auth.h index 700892c..857a1a1 100644 --- a/content/public/browser/android/devtools_auth.h +++ b/content/public/browser/android/devtools_auth.h @@ -6,14 +6,14 @@ #define CONTENT_PUBLIC_BROWSER_DEVTOOLS_AUTH_ANDROID_H_ #include "content/common/content_export.h" - -#include <pwd.h> +#include "net/socket/unix_domain_server_socket_posix.h" namespace content { -// Returns true if the given user/group pair is authorized to connect to the -// devtools server, false if not. -CONTENT_EXPORT bool CanUserConnectToDevTools(uid_t uid, gid_t gid); +// Returns true if the given peer identified by the credentials is authorized +// to connect to the devtools server, false if not. +CONTENT_EXPORT bool CanUserConnectToDevTools( + const net::UnixDomainServerSocket::Credentials& credentials); } // namespace content diff --git a/net/socket/unix_domain_client_socket_posix_unittest.cc b/net/socket/unix_domain_client_socket_posix_unittest.cc index d791122..d22aaf6 100644 --- a/net/socket/unix_domain_client_socket_posix_unittest.cc +++ b/net/socket/unix_domain_client_socket_posix_unittest.cc @@ -21,10 +21,14 @@ namespace { const char kSocketFilename[] = "socket_for_testing"; -bool UserCanConnectCallback(bool allow_user, uid_t uid, gid_t gid) { +bool UserCanConnectCallback( + bool allow_user, const UnixDomainServerSocket::Credentials& credentials) { // Here peers are running in same process. - EXPECT_EQ(getuid(), uid); - EXPECT_EQ(getgid(), gid); +#if defined(OS_LINUX) || defined(OS_ANDROID) + EXPECT_EQ(getpid(), credentials.process_id); +#endif + EXPECT_EQ(getuid(), credentials.user_id); + EXPECT_EQ(getgid(), credentials.group_id); return allow_user; } diff --git a/net/socket/unix_domain_listen_socket_posix.cc b/net/socket/unix_domain_listen_socket_posix.cc index 7bab4ff..dc7c19c 100644 --- a/net/socket/unix_domain_listen_socket_posix.cc +++ b/net/socket/unix_domain_listen_socket_posix.cc @@ -113,10 +113,9 @@ void UnixDomainListenSocket::Accept() { SocketDescriptor conn = StreamListenSocket::AcceptSocket(); if (conn == kInvalidSocket) return; - uid_t user_id; - gid_t group_id; - if (!UnixDomainServerSocket::GetPeerIds(conn, &user_id, &group_id) || - !auth_callback_.Run(user_id, group_id)) { + UnixDomainServerSocket::Credentials credentials; + if (!UnixDomainServerSocket::GetPeerCredentials(conn, &credentials) || + !auth_callback_.Run(credentials)) { if (IGNORE_EINTR(close(conn)) < 0) LOG(ERROR) << "close() error"; return; diff --git a/net/socket/unix_domain_listen_socket_posix_unittest.cc b/net/socket/unix_domain_listen_socket_posix_unittest.cc index 5fe5017..6fcbfd5 100644 --- a/net/socket/unix_domain_listen_socket_posix_unittest.cc +++ b/net/socket/unix_domain_listen_socket_posix_unittest.cc @@ -138,7 +138,7 @@ class TestListenSocketDelegate : public StreamListenSocket::Delegate { bool UserCanConnectCallback( bool allow_user, const scoped_refptr<EventManager>& event_manager, - uid_t, gid_t) { + const UnixDomainServerSocket::Credentials&) { event_manager->Notify( allow_user ? EVENT_AUTH_GRANTED : EVENT_AUTH_DENIED); return allow_user; diff --git a/net/socket/unix_domain_server_socket_posix.cc b/net/socket/unix_domain_server_socket_posix.cc index 8f2f2d6..a81f1ce 100644 --- a/net/socket/unix_domain_server_socket_posix.cc +++ b/net/socket/unix_domain_server_socket_posix.cc @@ -28,19 +28,20 @@ UnixDomainServerSocket::~UnixDomainServerSocket() { } // static -bool UnixDomainServerSocket::GetPeerIds(SocketDescriptor socket, - uid_t* user_id, - gid_t* group_id) { +bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket, + Credentials* credentials) { #if defined(OS_LINUX) || defined(OS_ANDROID) struct ucred user_cred; socklen_t len = sizeof(user_cred); if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0) return false; - *user_id = user_cred.uid; - *group_id = user_cred.gid; + credentials->process_id = user_cred.pid; + credentials->user_id = user_cred.uid; + credentials->group_id = user_cred.gid; return true; #else - return getpeereid(socket, user_id, group_id) == 0; + return getpeereid( + socket, &credentials->user_id, &credentials->group_id) == 0; #endif } @@ -130,10 +131,9 @@ bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket( scoped_ptr<StreamSocket>* socket) { DCHECK(accept_socket_); - uid_t user_id; - gid_t group_id; - if (!GetPeerIds(accept_socket_->socket_fd(), &user_id, &group_id) || - !auth_callback_.Run(user_id, group_id)) { + Credentials credentials; + if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) || + !auth_callback_.Run(credentials)) { accept_socket_.reset(); return false; } diff --git a/net/socket/unix_domain_server_socket_posix.h b/net/socket/unix_domain_server_socket_posix.h index 06fb8d3..85c743d 100644 --- a/net/socket/unix_domain_server_socket_posix.h +++ b/net/socket/unix_domain_server_socket_posix.h @@ -25,20 +25,30 @@ class SocketLibevent; // Linux and Android. class NET_EXPORT UnixDomainServerSocket : public ServerSocket { public: + // Credentials of a peer process connected to the socket. + struct NET_EXPORT Credentials { +#if defined(OS_LINUX) || defined(OS_ANDROID) + // Linux/Android API provides more information about the connected peer + // than Windows/OS X. It's useful for permission-based authorization on + // Android. + pid_t process_id; +#endif + uid_t user_id; + gid_t group_id; + }; + // Callback that returns whether the already connected client, identified by - // its process |user_id| and |group_id|, is allowed to keep the connection - // open. Note that the socket is closed immediately in case the callback - // returns false. - typedef base::Callback<bool (uid_t user_id, gid_t group_id)> AuthCallback; + // its credentials, is allowed to keep the connection open. Note that + // the socket is closed immediately in case the callback returns false. + typedef base::Callback<bool (const Credentials&)> AuthCallback; UnixDomainServerSocket(const AuthCallback& auth_callack, bool use_abstract_namespace); virtual ~UnixDomainServerSocket(); - // Gets UID and GID of peer to check permissions. - static bool GetPeerIds(SocketDescriptor socket_fd, - uid_t* user_id, - gid_t* group_id); + // Gets credentials of peer to check permissions. + static bool GetPeerCredentials(SocketDescriptor socket_fd, + Credentials* credentials); // ServerSocket implementation. virtual int Listen(const IPEndPoint& address, int backlog) OVERRIDE; diff --git a/net/socket/unix_domain_server_socket_posix_unittest.cc b/net/socket/unix_domain_server_socket_posix_unittest.cc index 07209f6..d8a5281 100644 --- a/net/socket/unix_domain_server_socket_posix_unittest.cc +++ b/net/socket/unix_domain_server_socket_posix_unittest.cc @@ -24,10 +24,14 @@ namespace { const char kSocketFilename[] = "socket_for_testing"; const char kInvalidSocketPath[] = "/invalid/path"; -bool UserCanConnectCallback(bool allow_user, uid_t uid, gid_t gid) { +bool UserCanConnectCallback(bool allow_user, + const UnixDomainServerSocket::Credentials& credentials) { // Here peers are running in same process. - EXPECT_EQ(getuid(), uid); - EXPECT_EQ(getgid(), gid); +#if defined(OS_LINUX) || defined(OS_ANDROID) + EXPECT_EQ(getpid(), credentials.process_id); +#endif + EXPECT_EQ(getuid(), credentials.user_id); + EXPECT_EQ(getgid(), credentials.group_id); return allow_user; } diff --git a/remoting/host/gnubby_auth_handler_posix.cc b/remoting/host/gnubby_auth_handler_posix.cc index f5673d3..04afc74 100644 --- a/remoting/host/gnubby_auth_handler_posix.cc +++ b/remoting/host/gnubby_auth_handler_posix.cc @@ -53,10 +53,10 @@ class CompareSocket { // Socket authentication function that only allows connections from callers with // the current uid. -bool MatchUid(uid_t user_id, gid_t) { - bool allowed = user_id == getuid(); +bool MatchUid(const net::UnixDomainServerSocket::Credentials& credentials) { + bool allowed = credentials.user_id == getuid(); if (!allowed) - HOST_LOG << "Refused socket connection from uid " << user_id; + HOST_LOG << "Refused socket connection from uid " << credentials.user_id; return allowed; } |