The API is protected with privilege http://tizen.org/privilege/internal/default/platform
This patch modifies internal IPC classes to handle blocking
operations so popup app calling ppm_send_popup_response can
receive answer in-place, waiting on socket data, using synchronous API.
Change-Id: If6a08f4b1a88b7259d5b31def08247e9e2e306d0
*
* @since_tizen 6.0
*
+ * @privlevel platform
+ * @privilege %http://tizen.org/privilege/internal/default/platform
+ *
* @param[in] popup_id The ID of the popup assigned by askuser daemon when UI popup app
* was invoked.
* @param[in] privacies The privacies array for which user response has been acquired.
printPrompt(Mode::SERVER);
}
- virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
+ virtual void extResponse(ConnectionFd fd, RequestId id, int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
// TODO
+ (void) fd;
+ (void) id;
(void) popup_id;
(void) privacies;
(void) responses;
int ret = p_client->impl->popupResponseExt(popup_id, vprivacies, vresponses);
- // TODO there is no privilege check on server side now
- // TODO we should be able to return ASKUSER_API_PERMISSION_DENIED in case when
- // caller doesn't have a privilege (ie. http://tizen.org/privilege/internal/default/platform)
+ if (ret == -EACCES)
+ return ASKUSER_API_PERMISSION_DENIED;
+ if (ret == -EINVAL)
+ return ASKUSER_API_INVALID_PARAM;
return ret != 0 ? ASKUSER_API_CONNECTION_ERROR : ASKUSER_API_SUCCESS;
});
}
* \since_tizen 6.0
*
* \privlevel platform
+ * \privilege %http://tizen.org/privilege/internal/default/platform
*
* \param[in] p_client An instance of the askuser_client structure
* previously created by askuser_client_initialize().
/*
- * Copyright (c) 2017 Samsung Electronics Co.
+ * Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace AskUser {
namespace Protocol {
-int Channel::process(int fd, int mask) {
+int Channel::process(int fd, int mask, bool block) {
try {
auto it = m_sockets.find(fd);
if (it == m_sockets.end()) {
}
if (mask & FdMask::READ) {
- int ret = desc.sock.recv(desc.input);
+ int ret = desc.sock.recv(desc.input, block);
if (ret <= 0) {
closeConnection(fd);
if (mask & FdMask::WRITE) {
int size = static_cast<int>(desc.output.size());
- int result = desc.sock.send(desc.output);
+ int result = desc.sock.send(desc.output, block);
if (result < 0) {
closeConnection(fd);
ALOGE("Cannot send a message on file descriptor: " << fd);
/*
- * Copyright (c) 2017 Samsung Electronics Co.
+ * Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* (FdMask::READ or FdMask::WRITE). If you pass 0 for some reason
* the descriptor will be closed and callback updateFd will be called
* with mask = 0
+ * \param[in] block Defines whether socket operation has to be blocking or not
* \return 0 on success
* \return a negative value in case of an error (see errno)
*/
- virtual int process(int fd, int mask);
+ virtual int process(int fd, int mask, bool block = false);
protected:
void parse(const std::string &data, std::vector<std::string> &parsedData);
namespace Protocol {
ClientChannel::ClientChannel(ClientCallbacksPtr ptr)
- : m_callbacks(std::move(ptr)) {
+ : m_callbacks(std::move(ptr)), m_synchronousCallStatus(RETURN_UNKNOWN) {
}
ClientChannel::~ClientChannel() {
std::copy(str.begin(), str.end(), std::back_inserter(m_sockets[fd].output));
int ret = process(fd, FdMask::WRITE);
+ if (ret) {
+ closeConnection(fd);
+ return ret;
+ }
+ // here, synchronously, we need to return answer (OK (1) or ACCESS_DENIED (0))
+ ret = process(fd, FdMask::READ, true);
+ // closeConnection should have been already called but we need to be sure
closeConnection(fd);
+ if (ret == -ENOTCONN)
+ switch (m_synchronousCallStatus) {
+ case RETURN_ACCESS_DENIED:
+ return -EACCES;
+ case RETURN_INVALID_PARAM:
+ return -EINVAL;
+ case RETURN_OK:
+ return 0;
+ case RETURN_UNKNOWN:
+ default:
+ return -ENOTCONN; // unknown issue with communication
+ }
+
+ if (!ret)
+ return -ENOTCONN;
+
return ret;
}
ALOGE("Server received empty message");
return -EINVAL;
}
-
+ m_synchronousCallStatus = RETURN_UNKNOWN;
int command = std::stoi(message[ASKUSER_MESSAGE_CMD_POS]);
switch (command) {
// after receiving the response just close the connection
closeConnection(fd);
+ return -ENOTCONN;
+ }
+ case MSGID_SEND_SYNC_MSG_RESULT:
+ {
+ if (message.size() != ASKUSER_MESSAGE_SEND_SYNC_MSG_RESULT_PARAM_COUNT) {
+ ALOGE("Inappropriate message size for MSGID_SEND_SYNC_MSG_RESULT command, size: " << message.size());
+ return -EINVAL;
+ }
+ m_synchronousCallStatus = std::stoi(message[ASKUSER_MESSAGE_CMD_POS + 1]);
+ closeConnection(fd);
+
return -ENOTCONN;
}
default :
AskUser::Protocol::RequestId generateRequestId();
ClientCallbacksPtr m_callbacks;
+ int m_synchronousCallStatus;
};
} // namespace Protocol
const int MSGID_TOAST2 = 3;
const int MSGID_EXT_POPUP_RESPONSE = 4;
const int MSGID_POPUP_RESPONSE = 1;
+const int MSGID_SEND_SYNC_MSG_RESULT = 2;
+
+// synchronous message return values from daemon
+const int RETURN_UNKNOWN = -1;
+const int RETURN_OK = 0;
+const int RETURN_ACCESS_DENIED = 1;
+const int RETURN_INVALID_PARAM = 2;
} // namespace Protocol
} // AskUser
MAX_RESPONSE_STATUS_LEN) +
SEP_LEN; // new line
-const std::size_t MIN_MESSAGE_LENGTH = std::min(MIN_EXT_RESPONSE_MESSAGE_LENGHT,std::min(MIN_REQUEST_MESSAGE_LENGTH, MIN_RESPONSE_MESSAGE_LENGTH));
-const std::size_t MAX_MESSAGE_LENGTH = std::max(MAX_EXT_RESPONSE_MESSAGE_LENGHT,std::max(MAX_REQUEST_MESSAGE_LENGTH, MAX_RESPONSE_MESSAGE_LENGTH));
+const std::size_t MIN_SYNC_MESSAGE_RESULT_LENGTH = MIN_COMMAND_LENGTH +
+ SEP_LEN + // space
+ MIN_RESPONSE_STATUS_LEN +
+ SEP_LEN; // new line
+
+const std::size_t MAX_SYNC_MESSAGE_RESULT_LENGTH = MAX_COMMAND_LENGTH +
+ SEP_LEN + // space
+ MAX_RESPONSE_STATUS_LEN +
+ SEP_LEN; // new line
+
+const std::size_t MIN_MESSAGE_LENGTH = std::min(std::min(MIN_SYNC_MESSAGE_RESULT_LENGTH, MIN_EXT_RESPONSE_MESSAGE_LENGHT),
+ std::min(MIN_REQUEST_MESSAGE_LENGTH, MIN_RESPONSE_MESSAGE_LENGTH));
+const std::size_t MAX_MESSAGE_LENGTH = std::max(std::max(MAX_SYNC_MESSAGE_RESULT_LENGTH, MAX_EXT_RESPONSE_MESSAGE_LENGHT),
+ std::max(MAX_REQUEST_MESSAGE_LENGTH, MAX_RESPONSE_MESSAGE_LENGTH));
const unsigned int ASKUSER_MESSAGE_CMD_POS = 0;
const unsigned int ASKUSER_MESSAGE_REQUESTID_POS = 1;
const unsigned int ASKUSER_MESSAGE_FIRST_PRIVILEGE_POS = 3;
const unsigned int ASKUSER_MESSAGE_MIN_MSG_PARAM_COUNT = 4; //cmd, requestId, privilegeCount, privilege
const unsigned int ASKUSER_MESSAGE_MIN_EXT_RESPONSE_MSG_PARAM_COUNT = 5; //cmd, popup_id, privacyCount, privacy, response
+const unsigned int ASKUSER_MESSAGE_SEND_SYNC_MSG_RESULT_PARAM_COUNT = 2; //cmd, result
const unsigned int ASKUSER_MESSAGE_EXT_RESPONSE_POPUP_ID_POS = 1;
const unsigned int ASKUSER_MESSAGE_EXT_RESPONSE_PRIV_COUNT_POS = 2;
const unsigned int ASKUSER_MESSAGE_EXT_RESPONSE_FIRST_PRIV_POS = 3;
}
}
+void ServerChannel::popupSendResponseResult(Protocol::ConnectionFd fd, Protocol::RequestId id, int answer) {
+ try {
+ (void)id;
+
+ auto it = m_sockets.find(fd);
+ if (it == m_sockets.end()) {
+ ALOGE("popupSendResponseResult: Cannot find file descriptor " << fd);
+ return;
+ }
+
+ auto &desc = it->second;
+
+ std::stringstream ss;
+ ss << MSGID_SEND_SYNC_MSG_RESULT << " " << answer << '\n';
+
+ std::string o = ss.str();
+ ALOGD("popupSendResponseResult: sending message: " << o);
+
+ std::copy(o.begin(), o.end(), std::back_inserter(desc.output));
+
+ m_callbacks->updateConnection(fd, FdMask::WRITE);
+ } catch (const std::exception &e){
+ ALOGE("Exception occurred during popupSendResponseResult: " << e.what());
+ }
+}
+
void ServerChannel::onAccept(int fd) {
m_callbacks->newConnection(fd, Credentials(fd));
m_callbacks->updateConnection(fd, FdMask::READ);
privacies[i] = base64Decode(message[ASKUSER_MESSAGE_EXT_RESPONSE_FIRST_PRIV_POS + i]);
responses[i] = std::stoi(message[ASKUSER_MESSAGE_EXT_RESPONSE_FIRST_PRIV_POS + privaciesCount + i]);
}
- m_callbacks->extResponse(popup_id, std::move(privacies), std::move(responses));
+ RequestId responseRequestId = popup_id; // not being sent over the connection, not need for it as this API is synchronous
+ // using it eitherway as Logic is indexing events with requestId
+ m_callbacks->extResponse(fd, responseRequestId, popup_id, std::move(privacies), std::move(responses));
break;
}
/**
* This function is called when external UI app retrived answer and askuser needs to answer to app
*
+ * \param[in] fd Connection file descriptor
+ * \param[in] id Request identifier
* \param[in] popup_id the ID (cookie) passed to UI app when requesting popup, identifies application request (its ConnectionFd and RequestId)
* \param[in] privacies the privacies for which the UI app is returning an answer (it can be a list smaller than originally requested)
* \param[in] responses the list of policy responses (Allow/Deny) for each privacy; vector size must be equal to privacies.size()
*/
- virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) = 0;
+ virtual void extResponse(Protocol::ConnectionFd fd, Protocol::RequestId id, int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) = 0;
};
typedef std::unique_ptr<IServerCallbacks> ServerCallbacksPtr;
virtual ~ServerChannel();
virtual void popupResponses(ConnectionFd fd, RequestId id, std::vector<int> &&responses);
+ virtual void popupSendResponseResult(Protocol::ConnectionFd fd, Protocol::RequestId id, int answer);
private:
virtual void onAccept(int fd);
/*
- * Copyright (c) 2017-2018 Samsung Electronics Co.
+ * Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return Sock(CLI_STREAM, retFd);
}
-int Sock::send(const RawBuffer &buffer) {
- static const int flags = MSG_NOSIGNAL | MSG_DONTWAIT;
+int Sock::send(const RawBuffer &buffer, bool block) {
+ static const int flags = block ? MSG_NOSIGNAL : (MSG_NOSIGNAL | MSG_DONTWAIT);
if (m_fd < 0) {
ALOGE("Send was called on closed socket");
return -1;
return 0;
}
-int Sock::recv(RawBuffer &output) {
+int Sock::recv(RawBuffer &output, bool block) {
if (m_fd < 0) {
ALOGE("Receive was called on closed socket");
return -1;
case SRV_DGRAM:
{
RawBuffer buffer(4096);
- int result = TEMP_FAILURE_RETRY(::recv(m_fd, buffer.data(), buffer.size(), MSG_DONTWAIT));
+ int result = TEMP_FAILURE_RETRY(::recv(m_fd, buffer.data(), buffer.size(), block ? 0 : MSG_DONTWAIT));
if (result > 0)
std::copy(buffer.begin(), buffer.begin()+result, std::back_inserter(output));
return static_cast<int>(result);
/*
- * Copyright (c) 2017 Samsung Electronics Co.
+ * Copyright (c) 2017-2020 Samsung Electronics Co., Ltd. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void close();
int wait(int mask);
- int recv(RawBuffer &buffer);
- int send(const RawBuffer &buffer);
+ int recv(RawBuffer &buffer, bool block = false);
+ int send(const RawBuffer &buffer, bool block = false);
private:
int getSocketFromSystemD() const;
}
}
- virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
+ virtual void extResponse(ConnectionFd fd, RequestId id,
+ PopupId popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
// TODO
+ (void) fd;
+ (void) id;
(void) popup_id;
(void) privacies;
(void) responses;
PKG_CHECK_MODULES(ASKUSER_NOTIFICATION_DEP
REQUIRED
+ cynara-client
elementary
libsystemd
vconf
ALOGD("Proper client connected");
stopTimer();
- ConnectionInfo connInfo{appId, pkgId, creds.uid, creds.pid, isHybrid};
+ ConnectionInfo connInfo{appId, pkgId, creds.uid, creds.label, creds.pid, isHybrid};
m_connToInfo.insert(it, std::make_pair(fd, connInfo));
} catch (const std::exception &e) {
ALOGE("Failed to add channel fd " << fd);
}
}
-void Logic::extResponse(Protocol::PopupId popup_id, const std::vector<Privacy> &privacies, const std::vector<int> &responses)
+void Logic::extResponse(Protocol::ConnectionFd fd, Protocol::RequestId id,
+ Protocol::PopupId popup_id,
+ const std::vector<Privacy> &privacies,
+ const std::vector<int> &responses)
{
ALOGD("Got response for popup_id : " << popup_id << " for privacies: ");
for (auto &p : privacies) {
ALOGD("---- privacy : " << p);
}
+ // To be set only if Cynara allowed to proceed
FdEvent respEvent;
size_t pendingEventsIndex = 0;
- for (FdEvent &ev : m_pendingEvents) {
- if (ev.popup_id == popup_id) {
- respEvent = ev;
- break;
- }
- ++pendingEventsIndex;
- }
- if (respEvent.popup_id == -1) {
- ALOGE("Got response for nonexisting popup_id : " << popup_id);
+ // 1st, lets check if the caller of ppm_popup_send_response has rights to do that
+ auto pit = m_connToInfo.find(fd);
+ if (pit == m_connToInfo.end()) {
+ ALOGE("Got request to non existing fd " << fd);
return;
}
+ ConnectionInfo &popup_conn = pit->second;
+
+ ALOGD("Checking cynara policy for client " << popup_conn.label);
+ std::string session = std::to_string(popup_conn.pid);
+ int ret = cynara_check(m_cynara,
+ popup_conn.label.c_str(),
+ session.c_str(),
+ popup_conn.user.c_str(),
+ "http://tizen.org/privilege/internal/default/platform");
+
+ switch (ret) {
+ case CYNARA_API_ACCESS_ALLOWED:
+ ALOGD("Connected client has permission to call ppm_popup_send_response");
+ for (FdEvent &ev : m_pendingEvents) {
+ if (ev.popup_id == popup_id) {
+ respEvent = ev;
+ break;
+ }
+ ++pendingEventsIndex;
+ }
+ if (respEvent.popup_id == -1) {
+ // Cynara said yes, but there is no paired event to answer to...
+ ALOGE("Got response for nonexisting popup_id: " << popup_id);
+ m_serverChannel->popupSendResponseResult(fd, id, Protocol::RETURN_INVALID_PARAM);
+ return;
+ }
+ // return OK to ppm_popup_send_response client
+ // & continue answering the app that initiated the popup
+ m_serverChannel->popupSendResponseResult(fd, id, Protocol::RETURN_OK);
+ break;
+ case CYNARA_API_ACCESS_DENIED:
+ ALOGD("Connected client doesn't have permission to call ppm_popup_send_response");
+ default:
+ m_serverChannel->popupSendResponseResult(fd, id, Protocol::RETURN_ACCESS_DENIED);
+ return;
+ }
auto it = m_eventInfo.find(respEvent.id);
-
if (it == m_eventInfo.end()) {
- ALOGE("Got response from strange event id ");
+ ALOGE("Got response from strange event id: " << respEvent.id.id);
return;
}
-
EventInfo &eventInfo = it->second;
auto itc = m_connToInfo.find(respEvent.id.fd);
-
if (itc == m_connToInfo.end()) {
- ALOGE("Got response from inactive fd " << respEvent.id.fd);
+ ALOGE("Got response from inactive fd: " << respEvent.id.fd);
return;
}
-
ConnectionInfo &conn = itc->second;
try {
Logic::~Logic()
{
m_serverChannel.reset();
+ (void)cynara_finish(m_cynara);
}
void Logic::registerSignalFd() {
#pragma once
+#include <cynara-client.h>
+#include <cynara-error.h>
+
#include <map>
#include <deque>
#include <server-channel.h>
private:
std::string m_msg;
};
- Logic() : m_timer(nullptr) {}
+ Logic() : m_timer(nullptr) {
+ (void)cynara_initialize(&m_cynara, nullptr);
+ }
void init();
void run();
void stop();
void popup(Protocol::ConnectionFd fd, Protocol::RequestId id, const std::vector<Privilege> &privileges);
- void extResponse(int popup_id, const std::vector<Privacy> &privacies, const std::vector<int> &responses);
+ void extResponse(Protocol::ConnectionFd fd, Protocol::RequestId id,
+ int popup_id,
+ const std::vector<Privacy> &privacies,
+ const std::vector<int> &responses);
~Logic();
private:
std::string appId;
std::string pkgId;
std::string user;
+ std::string label;
pid_t pid;
bool isHybrid;
};
std::map<EventId, EventInfo> m_eventInfo;
Ecore_Timer *m_timer;
+ cynara *m_cynara;
};
} // namespace Notification
m_service->popup(fd, id, privileges);
}
-void ServerCallbacks::extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
- m_service->extResponse(popup_id, privacies, responses);
+void ServerCallbacks::extResponse(Protocol::ConnectionFd fd, Protocol::RequestId id, int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses) {
+ m_service->extResponse(fd, id, popup_id, privacies, responses);
}
} /* namespace Notification */
virtual void newConnection(ConnectionFd fd, const Credentials &creds);
virtual void updateConnection(ConnectionFd fd, int mask);
virtual void popup(ConnectionFd fd, RequestId id, std::vector<std::string> &&privileges);
- virtual void extResponse(int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses);
+ virtual void extResponse(Protocol::ConnectionFd fd, Protocol::RequestId id,
+ int popup_id, std::vector<std::string> &&privacies, std::vector<int> &&responses);
virtual ~ServerCallbacks() {};
private: