Bug 1161020: Implement new socket-connector interface for key store, r=chucklee
authorThomas Zimmermann <tdz@users.sourceforge.net>
Tue, 19 May 2015 13:28:46 +0200
changeset 244513 85939a63419fe98cf95a23cfb1272f469d7914f6
parent 244512 0ba5a258797d88aa903092d008a03a3e8fd32cee
child 244514 b8c39f01f5f4780b3b77cc2cc0c3572b555c4794
push id28782
push userkwierso@gmail.com
push dateTue, 19 May 2015 23:42:58 +0000
treeherdermozilla-central@ac277e615f8f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschucklee
bugs1161020
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1161020: Implement new socket-connector interface for key store, r=chucklee This patch moves |KeyStoreConnector| into its own file and implements the new socket-connector interface.
ipc/keystore/KeyStore.cpp
ipc/keystore/KeyStore.h
ipc/keystore/KeyStoreConnector.cpp
ipc/keystore/KeyStoreConnector.h
ipc/keystore/moz.build
--- a/ipc/keystore/KeyStore.cpp
+++ b/ipc/keystore/KeyStore.cpp
@@ -14,16 +14,17 @@
 #include <android/log.h>
 #define KEYSTORE_LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
 #else
 #define KEYSTORE_LOG(args...)  printf(args);
 #endif
 
 #include "KeyStore.h"
 #include "jsfriendapi.h"
+#include "KeyStoreConnector.h"
 #include "MainThreadUtils.h" // For NS_IsMainThread.
 #include "nsICryptoHash.h"
 
 #include "plbase64.h"
 #include "certdb.h"
 #include "ScopedNSSTypes.h"
 
 using namespace mozilla::ipc;
@@ -299,17 +300,16 @@ void startKeyStoreService() { return; }
 
 static const char *CA_BEGIN = "-----BEGIN ",
                   *CA_END   = "-----END ",
                   *CA_TAILER = "-----\n";
 
 namespace mozilla {
 namespace ipc {
 
-static const char* KEYSTORE_SOCKET_PATH = "/dev/socket/keystore";
 static const char* KEYSTORE_ALLOWED_USERS[] = {
   "root",
   "wifi",
   NULL
 };
 static const char* KEYSTORE_ALLOWED_PREFIXES[] = {
   "WIFI_SERVERCERT_",
   "WIFI_USERCERT_",
@@ -667,86 +667,16 @@ checkPermission(uid_t uid)
     if (!strcmp(*user, userInfo->pw_name)) {
       return true;
     }
   }
 
   return false;
 }
 
-int
-KeyStoreConnector::Create()
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-
-  int fd;
-
-  unlink(KEYSTORE_SOCKET_PATH);
-
-  fd = socket(AF_LOCAL, SOCK_STREAM, 0);
-
-  if (fd < 0) {
-    NS_WARNING("Could not open keystore socket!");
-    return -1;
-  }
-
-  return fd;
-}
-
-bool
-KeyStoreConnector::CreateAddr(bool aIsServer,
-                              socklen_t& aAddrSize,
-                              sockaddr_any& aAddr,
-                              const char* aAddress)
-{
-  // Keystore socket must be server
-  MOZ_ASSERT(aIsServer);
-
-  aAddr.un.sun_family = AF_LOCAL;
-  if(strlen(KEYSTORE_SOCKET_PATH) > sizeof(aAddr.un.sun_path)) {
-      NS_WARNING("Address too long for socket struct!");
-      return false;
-  }
-  strcpy((char*)&aAddr.un.sun_path, KEYSTORE_SOCKET_PATH);
-  aAddrSize = strlen(KEYSTORE_SOCKET_PATH) + offsetof(struct sockaddr_un, sun_path) + 1;
-
-  return true;
-}
-
-bool
-KeyStoreConnector::SetUp(int aFd)
-{
-  // Socket permission check.
-  struct ucred userCred;
-  socklen_t len = sizeof(struct ucred);
-
-  if (getsockopt(aFd, SOL_SOCKET, SO_PEERCRED, &userCred, &len)) {
-    return false;
-  }
-
-  return ::checkPermission(userCred.uid);
-}
-
-bool
-KeyStoreConnector::SetUpListenSocket(int aFd)
-{
-  // Allow access of wpa_supplicant(different user, differnt group)
-  chmod(KEYSTORE_SOCKET_PATH, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
-
-  return true;
-}
-
-void
-KeyStoreConnector::GetSocketAddr(const sockaddr_any& aAddr,
-                                 nsAString& aAddrStr)
-{
-  // Unused.
-  MOZ_CRASH("This should never be called!");
-}
-
 //
 // KeyStore::ListenSocket
 //
 
 KeyStore::ListenSocket::ListenSocket(KeyStore* aKeyStore)
 : mKeyStore(aKeyStore)
 {
   MOZ_ASSERT(mKeyStore);
@@ -813,17 +743,17 @@ void
 KeyStore::StreamSocket::ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer)
 {
   mKeyStore->ReceiveSocketData(aBuffer);
 }
 
 ConnectionOrientedSocketIO*
 KeyStore::StreamSocket::GetIO()
 {
-  return PrepareAccept(new KeyStoreConnector());
+  return PrepareAccept(new KeyStoreConnector(KEYSTORE_ALLOWED_USERS));
 }
 
 //
 // KeyStore
 //
 
 KeyStore::KeyStore()
 : mShutdown(false)
@@ -872,17 +802,18 @@ KeyStore::Listen()
     mStreamSocket->Close();
   } else {
     mStreamSocket = new StreamSocket(this);
   }
 
   if (!mListenSocket) {
     // We only ever allocate one |ListenSocket|...
     mListenSocket = new ListenSocket(this);
-    mListenSocket->Listen(new KeyStoreConnector(), mStreamSocket);
+    mListenSocket->Listen(new KeyStoreConnector(KEYSTORE_ALLOWED_USERS),
+                          mStreamSocket);
   } else {
     // ... but keep it open.
     mListenSocket->Listen(mStreamSocket);
   }
 
   ResetHandlerInfo();
 }
 
--- a/ipc/keystore/KeyStore.h
+++ b/ipc/keystore/KeyStore.h
@@ -7,17 +7,16 @@
 #ifndef mozilla_ipc_KeyStore_h
 #define mozilla_ipc_KeyStore_h 1
 
 #include <sys/socket.h>
 #include <sys/un.h>
 #include "cert.h"
 #include "mozilla/ipc/ListenSocket.h"
 #include "mozilla/ipc/StreamSocket.h"
-#include "mozilla/ipc/UnixSocketConnector.h"
 #include "nsNSSShutDown.h"
 
 namespace mozilla {
 namespace ipc {
 
 enum ResponseCode {
   SUCCESS           =  1,
   LOCKED            =  2,
@@ -74,36 +73,16 @@ struct ProtocolParam{
 
 typedef enum {
   STATE_IDLE,
   STATE_READ_PARAM_LEN,
   STATE_READ_PARAM_DATA,
   STATE_PROCESSING
 } ProtocolHandlerState;
 
-class KeyStoreConnector : public mozilla::ipc::UnixSocketConnector
-{
-public:
-  KeyStoreConnector()
-  {}
-
-  virtual ~KeyStoreConnector()
-  {}
-
-  virtual int Create();
-  virtual bool CreateAddr(bool aIsServer,
-                          socklen_t& aAddrSize,
-                          sockaddr_any& aAddr,
-                          const char* aAddress);
-  virtual bool SetUp(int aFd);
-  virtual bool SetUpListenSocket(int aFd);
-  virtual void GetSocketAddr(const sockaddr_any& aAddr,
-                             nsAString& aAddrStr);
-};
-
 class KeyStore final : public nsNSSShutDownObject
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KeyStore)
 
   KeyStore();
 
   void Shutdown();
new file mode 100644
--- /dev/null
+++ b/ipc/keystore/KeyStoreConnector.cpp
@@ -0,0 +1,316 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et ft=cpp: tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "KeyStoreConnector.h"
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include "nsThreadUtils.h" // For NS_IsMainThread.
+
+#ifdef MOZ_WIDGET_GONK
+#include <android/log.h>
+#define KEYSTORE_LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
+#else
+#define KEYSTORE_LOG(args...)  printf(args);
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+static const char KEYSTORE_SOCKET_PATH[] = "/dev/socket/keystore";
+
+KeyStoreConnector::KeyStoreConnector(const char** const aAllowedUsers)
+  : mAllowedUsers(aAllowedUsers)
+{ }
+
+KeyStoreConnector::~KeyStoreConnector()
+{ }
+
+nsresult
+KeyStoreConnector::CreateSocket(int& aFd) const
+{
+  unlink(KEYSTORE_SOCKET_PATH);
+
+  aFd = socket(AF_LOCAL, SOCK_STREAM, 0);
+  if (aFd < 0) {
+    KEYSTORE_LOG("Could not open KeyStore socket!");
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::SetSocketFlags(int aFd) const
+{
+  static const int sReuseAddress = 1;
+
+  // Set close-on-exec bit.
+  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD));
+  if (flags < 0) {
+    return NS_ERROR_FAILURE;
+  }
+  flags |= FD_CLOEXEC;
+  int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags));
+  if (res < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Set non-blocking status flag.
+  flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
+  if (flags < 0) {
+    return NS_ERROR_FAILURE;
+  }
+  flags |= O_NONBLOCK;
+  res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags));
+  if (res < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Set socket addr to be reused even if kernel is still waiting to close.
+  res = setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &sReuseAddress,
+                   sizeof(sReuseAddress));
+  if (res < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::CheckPermission(int aFd) const
+{
+  struct ucred userCred;
+  socklen_t len = sizeof(userCred);
+
+  if (getsockopt(aFd, SOL_SOCKET, SO_PEERCRED, &userCred, &len)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const struct passwd* userInfo = getpwuid(userCred.uid);
+  if (!userInfo) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!mAllowedUsers) {
+    return NS_ERROR_FAILURE;
+  }
+
+  for (const char** user = mAllowedUsers; *user; ++user) {
+    if (!strcmp(*user, userInfo->pw_name)) {
+      return NS_OK;
+    }
+  }
+
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+KeyStoreConnector::CreateAddress(struct sockaddr& aAddress,
+                                 socklen_t& aAddressLength) const
+{
+  struct sockaddr_un* address =
+    reinterpret_cast<struct sockaddr_un*>(&aAddress);
+
+  size_t namesiz = strlen(KEYSTORE_SOCKET_PATH) + 1; // include trailing '\0'
+
+  if (namesiz > sizeof(address->sun_path)) {
+      KEYSTORE_LOG("Address too long for socket struct!");
+      return NS_ERROR_FAILURE;
+  }
+
+  address->sun_family = AF_UNIX;
+  memcpy(address->sun_path, KEYSTORE_SOCKET_PATH, namesiz);
+
+  aAddressLength = offsetof(struct sockaddr_un, sun_path) + namesiz;
+
+  return NS_OK;
+}
+
+// |UnixSocketConnector|
+
+nsresult
+KeyStoreConnector::ConvertAddressToString(const struct sockaddr& aAddress,
+                                          socklen_t aAddressLength,
+                                          nsACString& aAddressString)
+{
+  MOZ_ASSERT(aAddress.sa_family == AF_UNIX);
+
+  const struct sockaddr_un* un =
+    reinterpret_cast<const struct sockaddr_un*>(&aAddress);
+
+  size_t len = aAddressLength - offsetof(struct sockaddr_un, sun_path);
+
+  aAddressString.Assign(un->sun_path, len);
+
+  return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::CreateListenSocket(struct sockaddr* aAddress,
+                                      socklen_t* aAddressLength,
+                                      int& aListenFd)
+{
+  ScopedClose fd;
+
+  nsresult rv = CreateSocket(fd.rwget());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = SetSocketFlags(fd);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  if (aAddress && aAddressLength) {
+    rv = CreateAddress(*aAddress, *aAddressLength);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  // Allow access for wpa_supplicant (different user, different group)
+  //
+  // TODO: Improve this by setting specific user/group for
+  //       wpa_supplicant by calling |fchmod| and |fchown|.
+  //
+  chmod(KEYSTORE_SOCKET_PATH, S_IRUSR|S_IWUSR|
+                              S_IRGRP|S_IWGRP|
+                              S_IROTH|S_IWOTH);
+
+  aListenFd = fd.forget();
+
+  return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::AcceptStreamSocket(int aListenFd,
+                                      struct sockaddr* aAddress,
+                                      socklen_t* aAddressLength,
+                                      int& aStreamFd)
+{
+  ScopedClose fd(
+    TEMP_FAILURE_RETRY(accept(aListenFd, aAddress, aAddressLength)));
+  if (fd < 0) {
+    NS_WARNING("Cannot accept file descriptor!");
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = SetSocketFlags(fd);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = CheckPermission(fd);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  aStreamFd = fd.forget();
+
+  return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::CreateStreamSocket(struct sockaddr* aAddress,
+                                      socklen_t* aAddressLength,
+                                      int& aStreamFd)
+{
+  MOZ_CRASH("|KeyStoreConnector| does not support creating stream sockets.");
+  return NS_ERROR_FAILURE;
+}
+
+// Deprecated
+
+static const char* KEYSTORE_ALLOWED_USERS[] = {
+  "root",
+  "wifi",
+  NULL
+};
+
+static bool checkPermission(uid_t uid)
+{
+  struct passwd *userInfo = getpwuid(uid);
+  for (const char **user = KEYSTORE_ALLOWED_USERS; *user; user++ ) {
+    if (!strcmp(*user, userInfo->pw_name)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+int
+KeyStoreConnector::Create()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  int fd;
+
+  unlink(KEYSTORE_SOCKET_PATH);
+
+  fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+  if (fd < 0) {
+    NS_WARNING("Could not open keystore socket!");
+    return -1;
+  }
+
+  return fd;
+}
+
+bool
+KeyStoreConnector::CreateAddr(bool aIsServer,
+                              socklen_t& aAddrSize,
+                              sockaddr_any& aAddr,
+                              const char* aAddress)
+{
+  // Keystore socket must be server
+  MOZ_ASSERT(aIsServer);
+
+  aAddr.un.sun_family = AF_LOCAL;
+  if(strlen(KEYSTORE_SOCKET_PATH) > sizeof(aAddr.un.sun_path)) {
+      NS_WARNING("Address too long for socket struct!");
+      return false;
+  }
+  strcpy((char*)&aAddr.un.sun_path, KEYSTORE_SOCKET_PATH);
+  aAddrSize = strlen(KEYSTORE_SOCKET_PATH) + offsetof(struct sockaddr_un, sun_path) + 1;
+
+  return true;
+}
+
+bool
+KeyStoreConnector::SetUp(int aFd)
+{
+  // Socket permission check.
+  struct ucred userCred;
+  socklen_t len = sizeof(struct ucred);
+
+  if (getsockopt(aFd, SOL_SOCKET, SO_PEERCRED, &userCred, &len)) {
+    return false;
+  }
+
+  return checkPermission(userCred.uid);
+}
+
+bool
+KeyStoreConnector::SetUpListenSocket(int aFd)
+{
+  // Allow access of wpa_supplicant(different user, differnt group)
+  chmod(KEYSTORE_SOCKET_PATH, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+  return true;
+}
+
+void
+KeyStoreConnector::GetSocketAddr(const sockaddr_any& aAddr,
+                                 nsAString& aAddrStr)
+{
+  // Unused.
+  MOZ_CRASH("This should never be called!");
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/ipc/keystore/KeyStoreConnector.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et ft=cpp: tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_KeyStoreConnector_h
+#define mozilla_ipc_KeyStoreConnector_h
+
+#include "mozilla/ipc/UnixSocketConnector.h"
+
+namespace mozilla {
+namespace ipc {
+
+class KeyStoreConnector final : public UnixSocketConnector
+{
+public:
+  KeyStoreConnector(const char** const aAllowedUsers);
+  ~KeyStoreConnector();
+
+  // Methods for |UnixSocketConnector|
+  //
+
+  nsresult ConvertAddressToString(const struct sockaddr& aAddress,
+                                  socklen_t aAddressLength,
+                                  nsACString& aAddressString) override;
+
+  nsresult CreateListenSocket(struct sockaddr* aAddress,
+                              socklen_t* aAddressLength,
+                              int& aListenFd) override;
+
+  nsresult AcceptStreamSocket(int aListenFd,
+                              struct sockaddr* aAddress,
+                              socklen_t* aAddressLength,
+                              int& aStreamFd) override;
+
+  nsresult CreateStreamSocket(struct sockaddr* aAddress,
+                              socklen_t* aAddressLength,
+                              int& aStreamFd) override;
+
+  // Deprecated
+
+  int Create();
+  bool CreateAddr(bool aIsServer,
+                  socklen_t& aAddrSize,
+                  sockaddr_any& aAddr,
+                  const char* aAddress);
+  bool SetUp(int aFd);
+  bool SetUpListenSocket(int aFd);
+  void GetSocketAddr(const sockaddr_any& aAddr,
+                     nsAString& aAddrStr);
+
+private:
+  nsresult CreateSocket(int& aFd) const;
+  nsresult SetSocketFlags(int aFd) const;
+  nsresult CheckPermission(int aFd) const;
+  nsresult CreateAddress(struct sockaddr& aAddress,
+                         socklen_t& aAddressLength) const;
+
+  const char** const mAllowedUsers;
+};
+
+}
+}
+
+#endif
--- a/ipc/keystore/moz.build
+++ b/ipc/keystore/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.ipc += [
   'KeyStore.h'
 ]
 
 SOURCES += [
-  'KeyStore.cpp'
+  'KeyStore.cpp',
+  'KeyStoreConnector.cpp'
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'