diff --git a/ipc/keystore/KeyStore.cpp b/ipc/keystore/KeyStore.cpp index 1bf568b8521..0a878e221d2 100644 --- a/ipc/keystore/KeyStore.cpp +++ b/ipc/keystore/KeyStore.cpp @@ -19,6 +19,7 @@ #include "KeyStore.h" #include "jsfriendapi.h" +#include "KeyStoreConnector.h" #include "MainThreadUtils.h" // For NS_IsMainThread. #include "nsICryptoHash.h" @@ -304,7 +305,6 @@ static const char *CA_BEGIN = "-----BEGIN ", namespace mozilla { namespace ipc { -static const char* KEYSTORE_SOCKET_PATH = "/dev/socket/keystore"; static const char* KEYSTORE_ALLOWED_USERS[] = { "root", "wifi", @@ -672,76 +672,6 @@ checkPermission(uid_t uid) 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 // @@ -818,7 +748,7 @@ KeyStore::StreamSocket::ReceiveSocketData(nsAutoPtr& aBuffer) ConnectionOrientedSocketIO* KeyStore::StreamSocket::GetIO() { - return PrepareAccept(new KeyStoreConnector()); + return PrepareAccept(new KeyStoreConnector(KEYSTORE_ALLOWED_USERS)); } // @@ -877,7 +807,8 @@ KeyStore::Listen() 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); diff --git a/ipc/keystore/KeyStore.h b/ipc/keystore/KeyStore.h index 797cd536937..18dcf3b56ea 100644 --- a/ipc/keystore/KeyStore.h +++ b/ipc/keystore/KeyStore.h @@ -12,7 +12,6 @@ #include "cert.h" #include "mozilla/ipc/ListenSocket.h" #include "mozilla/ipc/StreamSocket.h" -#include "mozilla/ipc/UnixSocketConnector.h" #include "nsNSSShutDown.h" namespace mozilla { @@ -79,26 +78,6 @@ typedef enum { 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: diff --git a/ipc/keystore/KeyStoreConnector.cpp b/ipc/keystore/KeyStoreConnector.cpp new file mode 100644 index 00000000000..723fa2ca5ad --- /dev/null +++ b/ipc/keystore/KeyStoreConnector.cpp @@ -0,0 +1,321 @@ +/* -*- 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 +#include +#include +#include +#include "nsISupportsImpl.h" // For MOZ_COUNT_CTOR, MOZ_COUNT_DTOR +#include "nsThreadUtils.h" // For NS_IsMainThread. + +#ifdef MOZ_WIDGET_GONK +#include +#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) +{ + MOZ_COUNT_CTOR_INHERITED(KeyStoreConnector, UnixSocketConnector); +} + +KeyStoreConnector::~KeyStoreConnector() +{ + MOZ_COUNT_DTOR_INHERITED(KeyStoreConnector, UnixSocketConnector); +} + +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(&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(&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!"); +} + +} +} diff --git a/ipc/keystore/KeyStoreConnector.h b/ipc/keystore/KeyStoreConnector.h new file mode 100644 index 00000000000..31c71986268 --- /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 diff --git a/ipc/keystore/moz.build b/ipc/keystore/moz.build index 78af00cb211..62005f33aa8 100644 --- a/ipc/keystore/moz.build +++ b/ipc/keystore/moz.build @@ -9,7 +9,8 @@ EXPORTS.mozilla.ipc += [ ] SOURCES += [ - 'KeyStore.cpp' + 'KeyStore.cpp', + 'KeyStoreConnector.cpp' ] FAIL_ON_WARNINGS = True