From 19a8a12ba41746d30a20efb39d07679eae9162e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Opasiak Date: Mon, 29 Jun 2015 16:18:51 +0200 Subject: [PATCH] Add API for usb direct access granting Add new service for USB dev files access control Change-Id: If7d19d5e62c6aba6c81bd132a182e5ad4e768c3b Signed-off-by: Jan Cybulski Signed-off-by: Krzysztof Opasiak --- USD/src/CMakeLists.txt | 2 + USD/src/client/usb-access-client.cpp | 146 ++++++++++++++ USD/src/common/protocols.h | 6 +- USD/src/common/usb-access-map.cpp | 81 ++++++++ USD/src/common/usb-access-map.h | 48 +++++ USD/src/main/server2-main.cpp | 2 + USD/src/service/usb-access.cpp | 273 +++++++++++++++++++++++++++ USD/src/service/usb-access.h | 71 +++++++ USD/systemd/CMakeLists.txt | 1 + USD/systemd/usd-access.socket | 11 ++ USD/systemd/usd.service | 1 + packaging/capi-system-usbhost.spec | 3 + 12 files changed, 641 insertions(+), 4 deletions(-) create mode 100644 USD/src/client/usb-access-client.cpp create mode 100644 USD/src/service/usb-access.cpp create mode 100644 USD/src/service/usb-access.h create mode 100644 USD/systemd/usd-access.socket diff --git a/USD/src/CMakeLists.txt b/USD/src/CMakeLists.txt index 98871b3..260cfd1 100644 --- a/USD/src/CMakeLists.txt +++ b/USD/src/CMakeLists.txt @@ -15,6 +15,7 @@ SET(USD_SOURCES ${SERVER2_PATH}/main/generic-socket-manager.cpp ${SERVER2_PATH}/main/socket-manager.cpp ${SERVER2_PATH}/main/server2-main.cpp + ${SERVER2_PATH}/service/usb-access.cpp ) SET_SOURCE_FILES_PROPERTIES( @@ -63,6 +64,7 @@ INCLUDE_DIRECTORIES( SET(USD_CLIENT_SOURCES ${SERVER2_PATH}/client/client-common.cpp + ${SERVER2_PATH}/client/usb-access-client.cpp ) ADD_LIBRARY(${TARGET_USD_CLIENT} SHARED ${USD_CLIENT_SOURCES}) diff --git a/USD/src/client/usb-access-client.cpp b/USD/src/client/usb-access-client.cpp new file mode 100644 index 0000000..3d7f3bb --- /dev/null +++ b/USD/src/client/usb-access-client.cpp @@ -0,0 +1,146 @@ +/*-*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-*/ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Karol Lewandowski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file usb-access-client.cpp + * @author Jan Cybulski + * @author Krzysztof Opasiak + * @version 1.0 + * @brief This file is implementation of client functions for usb access service. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace { + +void create_msghdr(struct msghdr *hdr, + struct iovec *iov, + unsigned char *cmsgbuf, + const size_t cmsgbufSize, + int *retcode) + { + memset(hdr, 0, sizeof(*hdr)); + memset(cmsgbuf, 0, cmsgbufSize); + + iov->iov_base = retcode; + iov->iov_len = sizeof(*retcode); + hdr->msg_iov = iov; + hdr->msg_iovlen = 1; + + if (NULL != cmsgbuf) { + hdr->msg_control = cmsgbuf; + hdr->msg_controllen = cmsgbufSize; + } +} + +} // namespace anonymous + +_USD_API_ +int usd_open_usb_device(const char *devpath, int *fd) +{ + LogDebug("devpath: " << devpath); + using namespace USD; + return try_catch([&] { + MessageBuffer send; + struct msghdr hdr; + struct iovec iov; + unsigned char cmsgbuf[CMSG_SPACE(sizeof(int))]; + int retcode = -1; + int result; + + if (nullptr == devpath || nullptr == fd || !strlen(devpath)) { + LogError("Error input param."); + return USD_API_ERROR_INPUT_PARAM; + } + + Serialization::Serialize(send, + static_cast(USBAccessCall::USB_CALL_TYPE_OPEN)); + Serialization::Serialize(send, std::string(devpath)); + + create_msghdr(&hdr, &iov, &cmsgbuf[0], sizeof(cmsgbuf), &retcode); + + result = sendToServerAncData(SERVICE_SOCKET_USB_ACCESS, + send.Pop(), hdr); + if (result != USD_API_SUCCESS) { + *fd = -1; + return result; + } + + if (hdr.msg_flags & MSG_CTRUNC) { + LogError("Not enough space for ancillary element array."); + *fd = -1; + return USD_API_ERROR_BUFFER_TOO_SMALL; + } + + for(cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&hdr, cmsg)) { + if((SOL_SOCKET == cmsg->cmsg_level) && + (SCM_RIGHTS == cmsg->cmsg_type)) { + memmove(fd, CMSG_DATA(cmsg), sizeof(int)); + } + } + return retcode; + }); +} + +_USD_API_ +int usd_setup_usb_device_access(const char *topology, const char *smack, bool allow) { + LogDebug(" topology: " << topology << "smack: " << smack << "allow" << allow); + using namespace USD; + return try_catch([&] { + MessageBuffer send; + struct msghdr hdr; + struct iovec iov; + int retcode = -1; + + if (nullptr == topology || !strlen(topology) || + nullptr == smack || !strlen(smack)) { + LogError("Error input param."); + return USD_API_ERROR_INPUT_PARAM; + } + + Serialization::Serialize(send, + static_cast(USBAccessCall::USB_CALL_TYPE_SETUP_POLICY)); + Serialization::Serialize(send, std::string(smack)); + Serialization::Serialize(send, std::string(topology)); + Serialization::Serialize(send, allow); + + create_msghdr(&hdr, &iov, NULL, 0, &retcode); + + int result = sendToServerAncData(SERVICE_SOCKET_USB_ACCESS, send.Pop(), hdr); + if (result != USD_API_SUCCESS) + return result; + + if (hdr.msg_flags & MSG_CTRUNC) { + LogError("Not enough space for ancillary element array."); + return USD_API_ERROR_BUFFER_TOO_SMALL; + } + + return retcode; + }); +} diff --git a/USD/src/common/protocols.h b/USD/src/common/protocols.h index f106598..f417f60 100644 --- a/USD/src/common/protocols.h +++ b/USD/src/common/protocols.h @@ -28,11 +28,9 @@ namespace USD { -extern char const * const SERVICE_SOCKET_USB_ACCESS; +extern char const *const SERVICE_SOCKET_USB_ACCESS; - - -enum class UsbAccessCall +enum class USBAccessCall { USB_CALL_TYPE_OPEN, USB_CALL_TYPE_SETUP_POLICY diff --git a/USD/src/common/usb-access-map.cpp b/USD/src/common/usb-access-map.cpp index 75597ab..17e112f 100644 --- a/USD/src/common/usb-access-map.cpp +++ b/USD/src/common/usb-access-map.cpp @@ -307,4 +307,85 @@ std::ostream &operator<<(std::ostream &stream, return stream; } + +/****** PolicySubjectId ******/ + +PolicySubjectId::PolicySubjectId(const std::string &smk) + : m_smk(smk) +{ + /* Nothing to do here */ +} + +PolicySubjectId::PolicySubjectId(int socket) +{ + char *smk; + ssize_t ret; + + ret = smack_new_label_from_socket(socket, &smk); + if (ret < 0) + throw USD_API_ERROR_GETTING_SOCKET_LABEL_FAILED; + + m_smk = smk; + free(smk); +} + +PolicySubjectId::PolicySubjectId(IStream &str) +{ + Deserialization::Deserialize(str, m_smk); +} + +bool PolicySubjectId::operator<(const PolicySubjectId &r) const +{ + return this->m_smk < r.m_smk; +} + +bool PolicySubjectId::operator==(const PolicySubjectId &r) const +{ + return this->m_smk == r.m_smk; +} + +std::ostream& operator<<(std::ostream& stream, const PolicySubjectId& id) +{ + return stream << id.m_smk; +} + +/****** USBAccessMapKey ******/ + +USBAccessMapKey::USBAccessMapKey(const PolicySubjectId &subject, + const USBDeviceId &device) + : m_subject(subject), m_device(device) +{ + /* Nothing to do here */ +} + +bool USBAccessMapKey::validate() const +{ + /* TODO */ + return true; +} + +bool USBAccessMapKey::operator<(const USBAccessMapKey &r) const +{ + if (m_device != r.m_device) + return m_device < r.m_device; + + return m_subject < r.m_subject; +} + +bool USBAccessMapKey::operator==(const USBAccessMapKey &r) const +{ + if (m_device != r.m_device) + return false; + + return m_subject == r.m_subject; +} + +std::ostream& operator<<(std::ostream& stream, + const USBAccessMapKey& key) +{ + stream << "(" << key.m_subject << ", " << key.m_device << ")"; + return stream; +} + } /* namespace USD */ + diff --git a/USD/src/common/usb-access-map.h b/USD/src/common/usb-access-map.h index d6bb36f..5f7be9c 100644 --- a/USD/src/common/usb-access-map.h +++ b/USD/src/common/usb-access-map.h @@ -97,6 +97,54 @@ private: USBDevicePath m_path; }; +/** + * Application identifier, a subject of policy entry that is + * unique for a given app. + * + * As for now, this may be just smack label. + * If later this will be used on a multi-user system, we might add + * here additional field for user identifier. As for now it is not needed. + */ +class PolicySubjectId +{ +public: + PolicySubjectId(const std::string &smk); + PolicySubjectId(int socket); + PolicySubjectId(IStream &str); + + bool operator<(const PolicySubjectId &r) const; + bool operator==(const PolicySubjectId &r) const; + + friend std::ostream& operator<<(std::ostream& stream, + const PolicySubjectId& id); + +private: + std::string m_smk; +}; + +/** + * This is a key for searching in a USBAccessMap structure + */ +class USBAccessMapKey +{ +public: + USBAccessMapKey(const PolicySubjectId &subject, + const USBDeviceId &device); + bool validate(void) const; + + bool operator<(const USBAccessMapKey &r) const; + bool operator==(const USBAccessMapKey &r) const; + friend std::ostream& operator<<(std::ostream& stream, + const USBAccessMapKey& key); + +private: + PolicySubjectId m_subject; + USBDeviceId m_device; +}; + + +typedef std::map USBAccessMap; + } /* namespace USD */ #endif /* USB_ACCESS_MAP_H_ */ diff --git a/USD/src/main/server2-main.cpp b/USD/src/main/server2-main.cpp index da3eaa1..3429abb 100644 --- a/USD/src/main/server2-main.cpp +++ b/USD/src/main/server2-main.cpp @@ -30,6 +30,7 @@ #include +#include IMPLEMENT_SAFE_SINGLETON(USD::Log::LogSystem); @@ -77,6 +78,7 @@ int main(void) { LogInfo("Start!"); USD::SocketManager manager; + REGISTER_SOCKET_SERVICE(manager, USD::USBAccessService); manager.MainLoop(); } diff --git a/USD/src/service/usb-access.cpp b/USD/src/service/usb-access.cpp new file mode 100644 index 0000000..e84fe7a --- /dev/null +++ b/USD/src/service/usb-access.cpp @@ -0,0 +1,273 @@ +/*-*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-*/ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Karol Lewandowski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file usb-access.cpp + * @author Jan Cybulski + * @author Krzysztof Opasiak + * @version 1.0 + * @brief Implementation of service for granting usb raw data access. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { +// Service may open more than one socket. +// These ID's will be assigned to sockets +// and will be used only by service. +// When new connection arrives, AcceptEvent +// will be generated with proper ID to inform +// service about input socket. +// +// Please note: SocketManaged does not use it and +// does not check it in any way. +// +// If your service require only one socket +// (uses only one socket labeled with smack) +// you may ignore this ID (just pass 0) +const int SERVICE_SOCKET_ID = 0; + +} // namespace anonymous + + + +namespace USD { + +GenericSocketService::ServiceDescriptionVector +USBAccessService::GetServiceDescription() +{ + ServiceDescriptionVector description = { + { + /* service's socket path */ + .path = SERVICE_SOCKET_USB_ACCESS, + /* service's socket smack label */ + .smackLabel = "usd::api-usb-access", + /* service's interface id */ + .interfaceID = SERVICE_SOCKET_ID, + /* service is using sendMsg */ + .useSendMsg = true, + } + }; + + return description; +} + +void USBAccessService::accept(const AcceptEvent &event) +{ + LogDebug("Accept event. " + << "ConnectionID.sock: " << event.connectionID.sock + << " ConnectionID.counter: " << event.connectionID.counter + << " ServiceID: " << event.interfaceID); +} + +void USBAccessService::write(const WriteEvent &event) +{ + LogDebug("WriteEvent. " + << "ConnectionID: " << event.connectionID.sock + << " Size: " << event.size + << " Left: " << event.left); + + if (event.left == 0) + m_serviceManager->Close(event.connectionID); +} + +bool USBAccessService::checkAccessEntry(PolicySubjectId subjectId, + const USBDeviceId &devId) +{ + USBAccessMapKey key(subjectId, devId); + + LogDebug("checkAccessEntry begin"); + LogDebug("key: " << key); + + auto it = m_accessMap.find(key); + if (it == m_accessMap.end()) { + LogDebug("There is no entry in the database. Return deny."); + return false; + } + + LogDebug("There is an entry in a database. " + "Return value of policy: " << it->second); + + return it->second; +} + +static inline int openFileForClient(const char *path, int &fd) +{ + fd = TEMP_FAILURE_RETRY(open(path, O_RDWR)); + if (fd < 0) { + LogError("Cannot open file: " << path); + return USD_API_ERROR_FILE_NOT_EXIST; + } + + LogDebug("Opened file: '" << path << "' fd: " << fd); + return USD_API_SUCCESS; +} + +bool USBAccessService::processOpen(const ConnectionID &conn, + MessageBuffer &buffer) +{ + int fd = -1; + int retCode = USD_API_ERROR_SERVER_ERROR; + + LogDebug("Processing USB_CALL_TYPE_OPEN"); + + try { + std::string path; + bool accessGranted; + PolicySubjectId peerId(conn.sock); + + Deserialization::Deserialize(buffer, path); + + USBDeviceId devId(path, USBDevicePath::PATH_TYPE_DEV); + + accessGranted = checkAccessEntry(peerId, devId); + if (accessGranted) { + LogDebug("Access granted for opening: " + << path << " which belongs to: " << devId + << " by: " << peerId); + retCode = openFileForClient(path.c_str(), fd); + } else { + retCode = USD_API_ERROR_ACCESS_DENIED; + } + } catch(int e) { + retCode = e; + } + + /* Send the result */ + SendMsgData sendMsgData(retCode, fd); + m_serviceManager->Write(conn, sendMsgData); + /* We always return true because we have proccessed this message */ + return true; +} + +bool USBAccessService::processSetupPolicy(const ConnectionID &conn, + MessageBuffer &buffer) +{ + int retCode = USD_API_ERROR_SERVER_ERROR; + + LogDebug("Processing USB_CALL_TYPE_SETUP_POLICY"); + try { + std::string smack, path; + bool policy; + bool valid; + + Deserialization::Deserialize(buffer, smack); + Deserialization::Deserialize(buffer, path); + + USBAccessMapKey mk(smack, + USBDeviceId(path, USBDevicePath::PATH_TYPE_TOPO)); + + Deserialization::Deserialize(buffer, policy); + LogDebug("policy: " << policy + << ", smack: " << smack << ", path: " << path); + + valid = mk.validate(); + LogDebug("Validation" << (valid ? "ok" : "failed") + <<" on: " << mk << ", policy: " << policy); + if (valid) { + m_accessMap[mk] = policy; + retCode = USD_API_SUCCESS; + } else { + retCode = USD_API_ERROR_ACCESS_DENIED; + } + } catch(int e) { + LogDebug("Error occured on processing: " << e); + retCode = e; + } + + /* Send the result */ + SendMsgData sendMsgData(retCode, -1); + m_serviceManager->Write(conn, sendMsgData); + + /* We always return true because we have proccessed this message */ + return true; +} + +bool USBAccessService::processOne(const ConnectionID &conn, + MessageBuffer &buffer) +{ + LogDebug("Iteration begin"); + bool ret = true; + + if (!buffer.Ready()) + return false; + + Try { + USBAccessCall callType; + int tmp; + + Deserialization::Deserialize(buffer, tmp); + callType = static_cast(tmp); + + switch(callType) { + case USBAccessCall::USB_CALL_TYPE_OPEN: + ret = processOpen(conn, buffer); + break; + + case USBAccessCall::USB_CALL_TYPE_SETUP_POLICY: + ret = processSetupPolicy(conn, buffer); + break; + + default: + LogDebug("Broken protocol. Closing socket."); + m_serviceManager->Close(conn); + ret = false; + } + } Catch (MessageBuffer::Exception::Base) { + LogDebug("Broken protocol. Closing socket."); + m_serviceManager->Close(conn); + ret = false; + } + + return ret; +} + +void USBAccessService::process(const ReadEvent &event) +{ + LogDebug("Read event for counter: " << event.connectionID.counter); + + auto &buffer = m_messageBufferMap[event.connectionID.counter]; + buffer.Push(event.rawBuffer); + + /* + * We can get several requests in one package. + * Extract and process them all + */ + while(processOne(event.connectionID, buffer)); +} + +void USBAccessService::close(const CloseEvent &event) +{ + LogDebug("CloseEvent. ConnectionID: " << event.connectionID.sock); + m_messageBufferMap.erase(event.connectionID.counter); +} + +} // namespace USD + diff --git a/USD/src/service/usb-access.h b/USD/src/service/usb-access.h new file mode 100644 index 0000000..23fa677 --- /dev/null +++ b/USD/src/service/usb-access.h @@ -0,0 +1,71 @@ +/*-*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-*/ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Cybulski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file usb-access.h + * @author Jan Cybulski + * @author Krzysztof Opasiak + * @version 1.0 + * @brief Implementation of service for granting usb raw data access. + */ + +#ifndef USD_USB_ACCESS_H_ +#define USD_USB_ACCESS_H_ + +#include +#include +#include + +#include +#include +#include + +namespace USD { + +class USBAccessService : + public USD::GenericSocketService, + public USD::ServiceThread +{ +public: + typedef std::map MessageBufferMap; + + ServiceDescriptionVector GetServiceDescription(); + + DECLARE_THREAD_EVENT(AcceptEvent, accept) + DECLARE_THREAD_EVENT(WriteEvent, write) + DECLARE_THREAD_EVENT(ReadEvent, process) + DECLARE_THREAD_EVENT(CloseEvent, close) + + void accept(const AcceptEvent &event); + void write(const WriteEvent &event); + void process(const ReadEvent &event); + void close(const CloseEvent &event); + +private: + bool processOne(const ConnectionID &conn, MessageBuffer &buffer); + bool processOpen(const ConnectionID &conn, MessageBuffer &buffer); + bool processSetupPolicy(const ConnectionID &conn, MessageBuffer &buffer); + bool checkAccessEntry(PolicySubjectId subjectId, const USBDeviceId &devId); + + MessageBufferMap m_messageBufferMap; + USBAccessMap m_accessMap; +}; + +} /* namespace USD */ + +#endif /* USD_USB_ACCESS_H_ */ diff --git a/USD/systemd/CMakeLists.txt b/USD/systemd/CMakeLists.txt index 5cd9ce8..10cdc65 100644 --- a/USD/systemd/CMakeLists.txt +++ b/USD/systemd/CMakeLists.txt @@ -1,5 +1,6 @@ INSTALL(FILES ${CMAKE_SOURCE_DIR}/USD/systemd/usd.service + ${CMAKE_SOURCE_DIR}/USD/systemd/usd-access.socket DESTINATION ${SYSTEMD_DIR} ) diff --git a/USD/systemd/usd-access.socket b/USD/systemd/usd-access.socket new file mode 100644 index 0000000..eb39775 --- /dev/null +++ b/USD/systemd/usd-access.socket @@ -0,0 +1,11 @@ +[Socket] +ListenStream=/run/usd/usd-api-usb-access.socket +SocketMode=0777 +SmackLabelIPIn=* +SmackLabelIPOut=@ + +Service=usd.service + + +[Install] +WantedBy=sockets.target diff --git a/USD/systemd/usd.service b/USD/systemd/usd.service index b842e08..51dc8fa 100644 --- a/USD/systemd/usd.service +++ b/USD/systemd/usd.service @@ -4,6 +4,7 @@ Description=USB security daemon [Service] Type=notify ExecStart=/usr/bin/usd +Sockets=usd-access.socket [Install] WantedBy=multi-user.target diff --git a/packaging/capi-system-usbhost.spec b/packaging/capi-system-usbhost.spec index 08237fe..6d5a9a0 100644 --- a/packaging/capi-system-usbhost.spec +++ b/packaging/capi-system-usbhost.spec @@ -73,6 +73,7 @@ mkdir -p %{buildroot}%{_sysconfdir}/security/ mkdir -p %{buildroot}%{_unitdir}/multi-user.target.wants mkdir -p %{buildroot}%{_unitdir}/sockets.target.wants ln -s ../usd.service %{buildroot}%{_unitdir}/multi-user.target.wants/usd.service +ln -s ../usd-access.socket %{buildroot}%{_unitdir}/sockets.target.wants/usd-access.socket %post -p /sbin/ldconfig @@ -164,6 +165,8 @@ fi %{_libdir}/libusd-commons.so.* %attr(-,root,root) %{_unitdir}/multi-user.target.wants/usd.service %attr(-,root,root) %{_unitdir}/usd.service +%attr(-,root,root) %{_unitdir}/sockets.target.wants/usd-access.socket +%attr(-,root,root) %{_unitdir}/usd-access.socket %{_datadir}/license/%{name} %files libusd-client -- 2.34.1