2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include "internal/naive_policy_checker.hpp"
19 #include "internal/tslog.hpp"
21 #include "libdbuspolicy1-private.h"
24 #include <boost/utility/string_ref.hpp>
29 #include <dbuspolicy1/libdbuspolicy1.h>
33 #include <linux/kdbus.h>
34 #include <linux/limits.h>
40 #include <sys/types.h>
43 #define DBUSPOLICY1_EXPORT __attribute__ ((visibility("default")))
45 using ldp_xml_parser::DecisionResult;
46 using ldp_xml_parser::MatchItemSend;
47 using ldp_xml_parser::MatchItemReceive;
48 using ldp_xml_parser::MatchItemOwn;
49 using ldp_xml_parser::NaivePolicyChecker;
57 * This is a global structure that gets and keeps credentials from the current process,
58 * as they are needed for every policy checking operation.
66 std::call_once(init_once_done, std::bind(&udesc::init_once, this));
69 void change_creds(uid_t uid_, gid_t gid_, const char *label_)
74 if (sizeof(label) > strlen(label_))
75 memcpy(label, label_, strlen(label_) + 1);
77 LOGE("Error: failed to strcpy, length of label(%s) exceed 255", label_);
82 std::once_flag init_once_done;
89 attr_fd = open("/proc/self/attr/current", O_RDONLY);
91 assert(false && "failed open: /proc/self/attr/current");
92 r = read(attr_fd, buf, sizeof(buf));
95 if (r < 0 || r >= (long int)sizeof(label)) /* read */
96 assert(false && "failed read: /proc/self/attr/current");
101 snprintf(label, r + 1 /* additional byte for \0 */, "%s", buf);
107 extern "C" void __dbuspolicy1_change_creds(uid_t uid, gid_t gid, const char* label)
109 g_udesc.change_creds(uid, gid, label);
113 * This class decorates NaivePolicyChecker with initialization mechanisms.
114 * Thread-safety is ensured by C++ and static initialization in static member functions.
118 Checker(NaivePolicyChecker &checker, const char *primary_file_name, const char *secondary_file_name)
121 std::string primary_file_name_serialized = primary_file_name;
122 primary_file_name_serialized.append(".serialized");
124 auto ok = _checker.initDb(primary_file_name, primary_file_name_serialized.c_str());
126 ok = _checker.initDb(secondary_file_name);
128 assert(ok && "failed database initialization");
130 _checker.updateGroupDb(g_udesc.uid, g_udesc.gid);
133 NaivePolicyChecker &checker() { return _checker; }
135 // this checks if this process can open a bus owned by bus_owner
136 inline bool can_open_owned_by(uid_t bus_owner) {
137 return _checker.check(bus_owner, g_udesc.uid, g_udesc.gid, g_udesc.label) == DecisionResult::ALLOW;
140 static Checker &system() {
141 static Checker checker{policy_checker_system(), system_bus_conf_file_primary(), SYSTEM_BUS_CONF_FILE_SECONDARY};
145 static Checker &session() {
146 static Checker checker{policy_checker_session(), session_bus_conf_file_primary(), SESSION_BUS_CONF_FILE_SECONDARY};
150 static inline Checker &get(BusType bus_type) {
151 if (SESSION_BUS == bus_type)
157 NaivePolicyChecker &_checker;
161 * kconn is a class which binds together a kdbus connection and a corresponding policy checker.
162 * It has two flavors:
163 * - global objects for system bus and session bus with private connections (static get() method);
164 * - objects created on demand when a client shares his kdbus connection with the library (static get_shared() method).
166 * Because the pointer to such object is passed (through void *) to a client, and then clients
167 * pass the pointer to dbuspolicy1_* functions, we always need to maintain the same type,
168 * regardless of the flavor.
170 * Thread-safety is ensured by C++ and static initialization in static member functions.
174 KdbusConnection conn;
175 NaivePolicyChecker &checker;
178 kconn(NaivePolicyChecker &ch, const char *resolved_path)
179 : checker{ch}, can_be_freed{false}
181 if (conn.connect(resolved_path, 0, _KDBUS_ATTACH_ALL, 0) < 0) {
182 assert(false && "failed kdbus_connect");
186 kconn(NaivePolicyChecker &ch, int fd)
187 : checker{ch}, can_be_freed{true}
189 conn.shared_init_fd(fd);
192 static inline kconn &get(BusType bus_type, const char *resolved_path) {
193 if (SYSTEM_BUS == bus_type)
194 return system(resolved_path);
195 return session(resolved_path);
198 static inline kconn *get_shared(BusType bus_type, int fd)
200 kconn *result = new (std::nothrow) kconn(Checker::get(bus_type).checker(), fd);
201 if (nullptr == result)
202 LOGE("Error: failed to allocate memory for policy configuration");
207 static kconn &system(const char *resolved_path) {
208 static kconn c{Checker::system().checker(), resolved_path};
211 static kconn &session(const char *resolved_path) {
212 static kconn c{Checker::session().checker(), resolved_path};
219 * This class encapsulates functions used to analyze passed path to kdbus bus file.
220 * It recognizes system and session paths.
221 * If it recognizes path as a session bus, then it extracts owner's uid.
223 class BusPathResolver {
224 std::shared_ptr<const char> resolved_path;
225 BusType type{SESSION_BUS};
229 // We will never get 0 as a session bus owner (0 uses "0-system" bus),
230 // so it's OK to assume that bus_owner==0 always means error.
231 bool extract_bus_owner() noexcept
233 boost::string_ref user_suffix("-user/bus");
234 boost::string_ref ps(resolved_path.get());
236 if (!ps.ends_with(user_suffix))
239 ps.remove_suffix(user_suffix.length());
241 size_t last_slash = ps.find_last_of('/');
242 if (last_slash == boost::string_ref::npos || last_slash == ps.length() - 1)
245 ps = ps.substr(last_slash+1);
248 uid_t bus_owner = strtol(ps.data(), NULL, 10);
256 #define STR_SIZE(x) (x), sizeof(x)-1
257 static constexpr boost::string_ref KDBUS_PATH_PREFIX{STR_SIZE("/sys/fs/kdbus/")};
258 static constexpr boost::string_ref KDBUS_SYSTEM_BUS_PATH{STR_SIZE("/sys/fs/kdbus/0-system/bus")};
262 BusPathResolver(const char *bus_path)
263 : resolved_path(realpath(bus_path, nullptr), [](const char *a) { free(const_cast<char*>(a)); })
268 // recognize bus type
269 if (KDBUS_SYSTEM_BUS_PATH == resolved_path.get()) {
272 } else if (boost::string_ref(resolved_path.get()).starts_with(KDBUS_PATH_PREFIX)) {
273 ok = extract_bus_owner();
276 operator bool() { return ok; }
277 const char *path() { return resolved_path.get(); }
278 BusType bus_type() { return type; }
279 uid_t bus_owner() { return owner; }
282 constexpr boost::string_ref BusPathResolver::KDBUS_PATH_PREFIX;
283 constexpr boost::string_ref BusPathResolver::KDBUS_SYSTEM_BUS_PATH;
285 DBUSPOLICY1_EXPORT void* dbuspolicy1_init_shared(const char *bus_path, int fd)
289 BusPathResolver path_resolver(bus_path);
290 if (!path_resolver) {
291 LOGE("Error resolving bus path: %s", bus_path);
297 auto bus_type = path_resolver.bus_type();
299 kconn *result = nullptr;
300 if (Checker::get(bus_type).can_open_owned_by(path_resolver.bus_owner())) {
302 result = &kconn::get(bus_type, path_resolver.path());
304 result = kconn::get_shared(bus_type, fd);
313 DBUSPOLICY1_EXPORT void* dbuspolicy1_init(const char *bus_path)
315 return dbuspolicy1_init_shared(bus_path, -1);
320 kconn *KCONN(void *config) { return static_cast<kconn *>(config); }
323 * This function prepares contents of info.names() for initialization of MatchItems.
324 * First, it tries to get names from kdbus connection.
325 * If it fails, names are added from the 'name' parameter, which is treaten
326 * as a string containing names separated by single space.
328 inline int prepareNames(const char *name, KdbusConnectionInfo &info,
329 KdbusConnection::conn_info_type info_type) {
331 int r = info.get(name, info_type);
335 if (info.names().empty())
336 info.names().addSpaceSeparatedNames(name);
341 constexpr int decisionToRetCode(DecisionResult decision) {
342 return static_cast<int>(decision);
345 constexpr ldp_xml_parser::MessageType toMessageType(int type) {
346 return static_cast<ldp_xml_parser::MessageType>(type);
350 DBUSPOLICY1_EXPORT void dbuspolicy1_init_set_pool(void *configuration, void *pool)
352 KCONN(configuration)->conn.shared_init_pool(pool);
355 DBUSPOLICY1_EXPORT void dbuspolicy1_free(void* configuration)
357 auto kconn = KCONN(configuration);
358 if (kconn->can_be_freed)
362 DBUSPOLICY1_EXPORT int dbuspolicy1_check_out(void* configuration,
363 const char *destination,
366 const char *interface,
369 const char * /*error_name*/,
370 int /*reply_serial*/,
371 int /*requested_reply*/)
373 /* Broadcast signal has NULL destination */
374 /* Due to this sender can not check rule correctly */
375 if (message_type == DBUSPOLICY_MESSAGE_TYPE_SIGNAL && !destination)
378 tslog::LogLock log_lock;
379 auto kconn = KCONN(configuration);
380 KdbusConnectionInfo destinationInfo(kconn->conn);
383 /* if broadcasting, then pass - null destination */
384 r = prepareNames(destination, destinationInfo, KdbusConnection::POLICY_CONN_INFO_ALL);
388 auto m_type = toMessageType(message_type);
390 MatchItemSend sendItem(interface, member, path, m_type, destinationInfo.names());
391 auto decision = kconn->checker.check(g_udesc.uid, g_udesc.gid, g_udesc.label, sendItem);
393 if (DecisionResult::ALLOW != decision)
394 return decisionToRetCode(decision);
396 KdbusConnectionInfo senderInfo(kconn->conn);
398 /* get sender information from kdbus */
399 r = prepareNames(sender, senderInfo, KdbusConnection::POLICY_CONN_INFO_NAME);
403 MatchItemReceive receiveItem(interface, member, path, m_type, senderInfo.names());
404 return decisionToRetCode(kconn->checker.check(destinationInfo.uid(),
405 destinationInfo.gid(),
406 destinationInfo.label(),
410 DBUSPOLICY1_EXPORT int dbuspolicy1_check_in(void* configuration,
411 const char *destination,
413 const char *sender_label,
417 const char *interface,
420 const char * /*error_name*/,
421 int /*reply_serial*/,
422 int /*requested_reply*/)
424 tslog::LogLock log_lock;
425 auto kconn = KCONN(configuration);
426 KdbusConnectionInfo info(kconn->conn);
428 r = prepareNames(destination, info, KdbusConnection::POLICY_CONN_INFO_NAME);
432 auto m_type = toMessageType(message_type);
434 MatchItemSend sendItem(interface, member, path, m_type, info.names());
435 auto decision = kconn->checker.check(sender_uid, sender_gid, sender_label, sendItem);
437 if (DecisionResult::ALLOW != decision)
438 return decisionToRetCode(decision);
443 /* libdbus, gdbus pass multiple sender as parameter : eg. "name_A name_B name_C". */
445 MatchItemReceive receiveItem(interface, member, path, m_type, names.addSpaceSeparatedNames(sender));
447 return decisionToRetCode(kconn->checker.check(g_udesc.uid,
453 DBUSPOLICY1_EXPORT int dbuspolicy1_can_own(void* configuration, const char* const service)
455 tslog::LogLock log_lock;
456 auto kconn = KCONN(configuration);
457 return decisionToRetCode(kconn->checker.check(g_udesc.uid,
460 MatchItemOwn(service)));