From: Arek Antoniak Date: Fri, 11 Dec 2015 14:23:27 +0000 (+0100) Subject: Add libdbuspolicy library X-Git-Tag: accepted/tizen/ivi/20160218.023900~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F31%2F54131%2F4;p=platform%2Fcore%2Fsystem%2Flibdbuspolicy.git Add libdbuspolicy library Library implements dbus-daemon policy checking logic as standalone library, it complies with dbus-daemon policy specification and supports Tizen's Cynara extensions ("check" tag). libdbuspolicy uses kdbus directly to gain peer credentials. Authors: Kazimierz Krosman Arek Antoniak Change-Id: I7e637000a90b4194350b69d460ddb0da2b02b983 --- diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..d457aaa --- /dev/null +++ b/Makefile.am @@ -0,0 +1,68 @@ +EXTRA_DIST = +CLEANFILES = +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +AM_MAKEFLAGS = --no-print-directory + +AM_CXXFLAGS = ${my_CXXFLAGS} + +AM_CPPFLAGS = \ + -include $(top_builddir)/config.h \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + $(CYNARA_CFLAGS) \ + -I${top_srcdir}/src + +AM_CFLAGS = ${my_CFLAGS} \ + -fvisibility=hidden \ + -ffunction-sections \ + $(CYNARA_CFLAGS) \ + -fdata-sections + +AM_LDFLAGS = \ + -Wl,--gc-sections \ + -Wl,--as-needed \ + $(CYNARA_CFLAGS) \ +$(CYNARA_LIBS)\ + -pthread + +SED_PROCESS = \ + $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(SED) \ + -e 's,@VERSION\@,$(VERSION),g' \ + -e 's,@prefix\@,$(prefix),g' \ + -e 's,@exec_prefix\@,$(exec_prefix),g' \ + -e 's,@libdir\@,$(libdir),g' \ + -e 's,@includedir\@,$(includedir),g' \ + < $< > $@ || rm $@ + +%.pc: %.pc.in Makefile + $(SED_PROCESS) + +LIBDBUSPOLICY1_CURRENT=1 +LIBDBUSPOLICY1_REVISION=0 +LIBDBUSPOLICY1_AGE=0 + +pkginclude_HEADERS = src/dbuspolicy1/libdbuspolicy1.h +lib_LTLIBRARIES = src/libdbuspolicy1.la + +src_libdbuspolicy1_la_SOURCES =\ + src/libdbuspolicy1-private.h \ + src/libdbuspolicy1.c \ + src/internal/internal.cpp + +EXTRA_DIST += src/libdbuspolicy1.sym + +src_libdbuspolicy1_la_LDFLAGS = $(AM_LDFLAGS) \ + -version-info $(LIBDBUSPOLICY1_CURRENT):$(LIBDBUSPOLICY1_REVISION):$(LIBDBUSPOLICY1_AGE) \ + $(CYNARA_LIBS) \ + -Wl,--version-script=$(top_srcdir)/src/libdbuspolicy1.sym +src_libdbuspolicy1_la_DEPENDENCIES = ${top_srcdir}/src/libdbuspolicy1.sym + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = src/libdbuspolicy1.pc +EXTRA_DIST += src/libdbuspolicy1.pc.in +CLEANFILES += src/libdbuspolicy1.pc + +TESTS = src/test-libdbuspolicy1 + +check_PROGRAMS = src/test-libdbuspolicy1 +src_test_libdbuspolicy1_SOURCES = src/test-libdbuspolicy1.c +src_test_libdbuspolicy1_LDADD = src/libdbuspolicy1.la $(CYNARA_LIBS) diff --git a/README b/README new file mode 100644 index 0000000..39b4067 --- /dev/null +++ b/README @@ -0,0 +1,86 @@ +Authors: + Kazimierz Krosman + Arek Antoniak + +Library libdbuspolicy adds functionality of DBUS policy utilization in KDBUS, it complies with DBUS policy specification and supports Cynara check directive. + +Usage: + ./autogen.sh + ./configure + make + make install + +The library delivers following API: + + /*! + libdbuspolicy init + \param bus_type bus type (SYSTEM or SESSION) + */ + void* dbuspolicy1_init(unsigned int bus_type); + + /*! + libdbuspolicy free + \param configuration pointer with policy configuration acquired using dbuspolicy1_init + */ + void dbuspolicy1_free(void* configuration); + + /*! + Check policy for outgoing message + \param configuration pointer with policy configuration + \param destination list of message destinations + \param sender list of message sender names + \param path path + \param interface interface name + \param member member name + \param message_type message type + \param error_name (future implementation) + \param reply_serial (future implementation) + \param requested_reply (future implementation) + */ + int dbuspolicy1_check_out(void* configuration, + const char *destination, + const char *sender, + const char *path, + const char *interface, + const char *member, + int message_type, + const char *error_name, + int reply_serial, + int requested_reply); + + /*! + Check policy for incoming message + \param configuration pointer with policy configuration + \param destination list of message destinations + \param sender list of message sender names + \param sender_label sender label (should be manually extracted from incomming message) + \param sender_uid sender uid (should be manually extracted from incomming message) + \param sender_gid sender gid (should be manually extracted from incomming message) + \param path path + \param interface interface name + \param member member name + \param message_type message type + \param error_name (future implementation) + \param reply_serial (future implementation) + \param requested_reply (future implementation) + */ + int dbuspolicy1_check_in(void* configuration, + const char *destination, + const char *sender, + const char *sender_label, + uid_t sender_uid, + gid_t sender_gid, + const char *path, + const char *interface, + const char *member, + int message_type, + const char *error_name, + int reply_serial, + int requested_reply); + + /*! + Check policy for service ownership + \param configuration pointer with policy configuration + \param service service name + */ + int dbuspolicy1_can_own(void* configuration, const char* const service); diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..16c0eb3 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -e + +if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then + cp -p .git/hooks/pre-commit.sample .git/hooks/pre-commit && \ + chmod +x .git/hooks/pre-commit && \ + echo "Activated pre-commit hook." +fi + +autoreconf --install --symlink + +libdir() { + echo $(cd $1/$(gcc -print-multi-os-directory); pwd) +} + +args="--prefix=/usr \ +--sysconfdir=/etc \ +--libdir=$(libdir /usr/lib)" + +echo +echo "----------------------------------------------------------------" +echo "Initialized build system. For a common configuration please run:" +echo "----------------------------------------------------------------" +echo +echo "./configure CFLAGS='-g -O0' $args" +echo diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..440b0c2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,104 @@ +AC_PREREQ(2.60) +AC_INIT([dbuspolicy], + [1], + [dev@lists.tizen.org], + [dbuspolicy], + [http://review.tizen.org]) +AC_CONFIG_SRCDIR([src/libdbuspolicy1.c]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([ + check-news + foreign + 1.11 + -Wall + -Wno-portability + silent-rules + tar-pax + no-dist-gzip + dist-xz + subdir-objects +]) +AC_PROG_CC_STDC +AC_PROG_CXX +AC_USE_SYSTEM_EXTENSIONS +AC_SYS_LARGEFILE +AC_CONFIG_MACRO_DIR([m4]) +AM_SILENT_RULES([yes]) +LT_INIT([ + disable-static + pic-only +]) +AC_PREFIX_DEFAULT([/usr]) + +AC_PROG_SED +AC_PROG_MKDIR_P + +AC_ARG_ENABLE([logging], + AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), + [], enable_logging=yes) +AS_IF([test "x$enable_logging" = "xyes"], [ + AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) +]) + +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), + [], [enable_debug=no]) +AS_IF([test "x$enable_debug" = "xyes"], [ + AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) +]) + +AC_CHECK_FUNCS([ \ + __secure_getenv \ + secure_getenv\ +]) + +my_CFLAGS="\ +-Wall \ +-Wchar-subscripts \ +-Wformat-security \ +-Wmissing-declarations \ +-Wmissing-prototypes \ +-Wnested-externs \ +-Wpointer-arith \ +-Wshadow \ +-Wsign-compare \ +-Wstrict-prototypes \ +-Wtype-limits \ +" +AC_SUBST([my_CFLAGS]) + +my_CXXFLAGS="\ +-Wall \ +-std=c++11 \ +" + +AC_SUBST([my_CXXFLAGS]) +PKG_CHECK_MODULES([CYNARA], [cynara-client >= 0.4.2 cynara-session >= 0.4.2], + [AC_DEFINE([ENABLE_CYNARA], [1], [Define to enable Cynara privilege checks in libdbuspolicy])], + [AC_MSG_ERROR([libcynara-client-async and cynara-session are required to enable Cynara integration])]) + +AC_SUBST([CYNARA_CFLAGS]) +AC_SUBST([CYNARA_LIBS]) + +AC_CONFIG_HEADERS(config.h) +AC_CONFIG_FILES([ + Makefile +]) + +AC_OUTPUT +AC_MSG_RESULT([ + $PACKAGE $VERSION + ===== + + prefix: ${prefix} + sysconfdir: ${sysconfdir} + libdir: ${libdir} + includedir: ${includedir} + + compiler: ${CC} + cflags: ${CFLAGS} ${CYNARA_CFLAGS} + ldflags: ${LDFLAGS} ${CYMARA_LIBS} + + logging: ${enable_logging} + debug: ${enable_debug} +]) diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..38066dd --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,5 @@ +libtool.m4 +ltoptions.m4 +ltsugar.m4 +ltversion.m4 +lt~obsolete.m4 diff --git a/packaging/libdbuspolicy.spec b/packaging/libdbuspolicy.spec new file mode 100644 index 0000000..f38aadc --- /dev/null +++ b/packaging/libdbuspolicy.spec @@ -0,0 +1,43 @@ +Name: libdbuspolicy +Summary: Helper library for fine-grained userspace policy handling +License: Apache-2.0 +Group: Base/IPC +Version: 1.0.0 +Release: 0 + +Source: %{name}-%{version}.tar.gz +BuildRequires: boost-devel +BuildRequires: pkgconfig(cynara-client) + + +%description +libdbuspolicy is a helper library for fine-grained userspace +policy handling (with SMACK support) + +%prep +%setup -q + +%build +./autogen.sh +./configure \ + --libdir=%{_libdir} \ + --prefix=/usr + +make + +%install +make DESTDIR=%{buildroot} install-strip + +%post -p /sbin/ldconfig +%postun -p /sbin/ldconfig + +%files +%defattr(-,root,root) +%{_includedir}/* + +%{_libdir}/pkgconfig/* +%{_libdir}/libdbuspolicy1.so.* +%{_libdir}/libdbuspolicy1.la +%{_libdir}/libdbuspolicy1.so + +%changelog diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..1a03cff --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,7 @@ +.dirstamp +.deps/ +.libs/ +*.la +*.lo +libdbuspolicy1.pc +test-libdbuspolicy1 diff --git a/src/dbuspolicy1/libdbuspolicy1.h b/src/dbuspolicy1/libdbuspolicy1.h new file mode 100644 index 0000000..d135e50 --- /dev/null +++ b/src/dbuspolicy1/libdbuspolicy1.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _LIBDBUSPOLICY1_H_ +#define _LIBDBUSPOLICY1_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SYSTEM_BUS_CONF_FILE "/etc/dbus-1/system.conf" +#define SESSION_BUS_CONF_FILE "/etc/dbus-1/session.conf" + +#define SYSTEM_BUS 1 +#define SESSION_BUS 2 + +/** used when check policy for message prepared to send */ +#define DBUSPOLICY_DIRECTION_SENDING 0 + + /** used when check policy for message read from bus */ +#define DBUSPOLICY_DIRECTION_RECEIVING 1 + +#define DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL 1 +#define DBUSPOLICY_MESSAGE_TYPE_METHOD_RETURN 2 +#define DBUSPOLICY_MESSAGE_TYPE_ERROR 3 +#define DBUSPOLICY_MESSAGE_TYPE_SIGNAL 4 + +struct udesc; + +/*! + libdbuspolicy init + \param bus_type bus type (SYSTEM or SESSION) + */ +void* dbuspolicy1_init(unsigned int bus_type); + +/*! + libdbuspolicy free + \param configuration pointer with policy configuration acquired using dbuspolicy1_init + */ +void dbuspolicy1_free(void* configuration); + +/*! + Check policy for outgoing message + \param configuration pointer with policy configuration + \param destination list of message destinations + \param sender list of message sender names + \param path path + \param interface interface name + \param member member name + \param message_type message type + \param error_name (future implementation) + \param reply_serial (future implementation) + \param requested_reply (future implementation) + */ +int dbuspolicy1_check_out(void* configuration, + const char *destination, + const char *sender, + const char *path, + const char *interface, + const char *member, + int message_type, + const char *error_name, + int reply_serial, + int requested_reply); + +/*! + Check policy for incoming message + \param configuration pointer with policy configuration + \param destination list of message destinations + \param sender list of message sender names + \param sender_label sender label (should be manually extracted from incomming message) + \param sender_uid sender uid (should be manually extracted from incomming message) + \param sender_gid sender gid (should be manually extracted from incomming message) + \param path path + \param interface interface name + \param member member name + \param message_type message type + \param error_name (future implementation) + \param reply_serial (future implementation) + \param requested_reply (future implementation) + */ +int dbuspolicy1_check_in(void* configuration, + const char *destination, + const char *sender, + const char *sender_label, + uid_t sender_uid, + gid_t sender_gid, + const char *path, + const char *interface, + const char *member, + int message_type, + const char *error_name, + int reply_serial, + int requested_reply); + +/*! + Check policy for service ownership + \param configuration pointer with policy configuration + \param service service name + */ +int dbuspolicy1_can_own(void* configuration, const char* const service); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/internal/cynara.hpp b/src/internal/cynara.hpp new file mode 100644 index 0000000..7f74f35 --- /dev/null +++ b/src/internal/cynara.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _CYNARA_HPP +#define _CYNARA_HPP + +#include +#include + +namespace _ldp_cynara { + class Cynara { + private: + cynara* __cynara; + std::string __session; + + Cynara() { + int r = cynara_initialize(&__cynara, NULL); + if (r != CYNARA_API_SUCCESS) + throw std::runtime_error("Cynara initialization failed"); + + __session = cynara_session_from_pid(getpid()); + } + + ~Cynara() { + int r = cynara_finish(__cynara); + if (r != CYNARA_API_SUCCESS) { + //TODO: reaction + } + } + + static Cynara& get_instance() { + static Cynara __self; + return __self; + } + + public: + static std::string get_session() { + Cynara& c = Cynara::get_instance(); + c.__session = cynara_session_from_pid(getpid()); + return c.__session; + } + + static bool check(std::string label, std::string privilege, std::string uid, std::string session = "") { + Cynara& c = Cynara::get_instance(); + const char* _label=""; + const char* _session=""; + const char* _uid=""; + const char* _privilege=""; + + /** + workaround. C-str() returns wrong pointer to str + when std::string == "" + */ + if (!label.empty()) + _label=label.c_str(); + + if (session == "") + session = c.__session; + if (!session.empty()) + _session=session.c_str(); + + if (!privilege.empty()) + _privilege=privilege.c_str(); + + if (!uid.empty()) + _uid=uid.c_str(); + + int r = cynara_check (c.__cynara, _label, _session, _uid, _privilege); + if (r == CYNARA_API_ACCESS_ALLOWED) + return true; + else if (r == CYNARA_API_ACCESS_DENIED) + return false; + else + throw std::runtime_error("Cynara check failed"); + } + }; +} //namespace +#endif diff --git a/src/internal/internal.cpp b/src/internal/internal.cpp new file mode 100644 index 0000000..e5e5a18 --- /dev/null +++ b/src/internal/internal.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#include +#include +#include +#include "xml_parser.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +static const char* get_bus(int bus_type) { + return (bus_type == SYSTEM_BUS) ? "SYSTEM" : "SESSION"; +} + +static const char* get_str(const char* const szstr) { + return (szstr != NULL) ? szstr : ""; +} + +static const char* get_message_type(int type) { + const char* sztype; + switch(type) { + case DBUSPOLICY_MESSAGE_TYPE_METHOD_CALL: sztype = "method_call"; break; + case DBUSPOLICY_MESSAGE_TYPE_METHOD_RETURN: sztype = "method_return"; break; + case DBUSPOLICY_MESSAGE_TYPE_ERROR: sztype = "error"; break; + case DBUSPOLICY_MESSAGE_TYPE_SIGNAL: sztype = "signal"; break; + default: sztype = ""; break; + } + return sztype; +} + +int __internal_init(unsigned int bus_type, const char* const config_name) +{ + _ldp_xml_parser::XmlAsyncParser p; + auto err = p.parse_policy(get_bus(bus_type), get_str(config_name), std::chrono::milliseconds(10000)); + return err.get(); +} + +int __internal_can_send(unsigned int bus_type, + const char* const user, + const char* const group, + const char* const label, + const char* const destination, + const char* const path, + const char* const interface, + const char* const member, + int type) +{ + _ldp_xml_parser::XmlAsyncParser p; + auto err = p.can_send(get_bus(bus_type), get_str(user), get_str(group), get_str(label), get_str(destination), get_str(path), get_str(interface), get_str(member), get_message_type(type)); + return err.get(); +} + +int __internal_can_recv(unsigned int bus_type, + const char* const user, + const char* const group, + const char* const label, + const char* const sender, + const char* const path, + const char* const interface, + const char* const member, + int type) +{ + _ldp_xml_parser::XmlAsyncParser p; + auto err = p.can_recv(get_bus(bus_type), get_str(user), get_str(group), get_str(label), get_str(sender), get_str(path), get_str(interface), get_str(member), get_message_type(type)); + return err.get(); +} + +int __internal_can_own(unsigned int bus_type, + const char* const user, + const char* const group, + const char* const service) +{ + _ldp_xml_parser::XmlAsyncParser p; + auto err = p.can_own(get_bus(bus_type), get_str(user), get_str(group), get_str(service)); + return err.get(); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/src/internal/internal.h b/src/internal/internal.h new file mode 100644 index 0000000..84a1069 --- /dev/null +++ b/src/internal/internal.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _LIBDBUSPOLICY1_INTERNAL_H_ +#define _LIBDBUSPOLICY1_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int __internal_init(unsigned int bus_type, const char* const config_name); + +int __internal_can_send(unsigned int bus_type, + const char* const user, + const char* const group, + const char* const label, + const char* const destination, + const char* const path, + const char* const interface, + const char* const member, + int type); + +int __internal_can_recv(unsigned int bus_type, + const char* const user, + const char* const group, + const char* const label, + const char* const sender, + const char* const path, + const char* const interface, + const char* const member, + int type); + +int __internal_can_own(unsigned int bus_type, + const char* const user, + const char* const group, + const char* const service); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/internal/libdbuspolicy1-private.hpp b/src/internal/libdbuspolicy1-private.hpp new file mode 100644 index 0000000..4b467b4 --- /dev/null +++ b/src/internal/libdbuspolicy1-private.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _LIBDBUSPOLICY1_PRIVATE_HPP +#define _LIBDBUSPOLICY1_PRIVATE_HPP + +#include + +namespace { + class ErrCode { + int m_err; + std::string m_err_str; + ErrCode(int e, const std::string& s) : m_err(e), m_err_str(s) {} + public: + ErrCode() : m_err(0), m_err_str("") {} + virtual ~ErrCode() {} + + static ErrCode ok() { + return ErrCode(0, "OK"); + } + + template + static ErrCode ok(T e) { + return ErrCode((e > 0) ? e : 0, "OK"); + } + + static ErrCode error(const std::string& what) { + return ErrCode(-1, what); + } + + static ErrCode timeout(const std::string& what) { + return ErrCode(-99, std::string("Timeout: ") + what); + } + + int get() const { + return m_err; + } + + const std::string& get_str() const { + return m_err_str; + } + + bool is_ok() const { + return (m_err >= 0); + } + + bool is_true() const { + return (m_err > 0); + } + + bool is_false() const { + return (m_err == 0); + } + + bool is_error() const { + return (m_err < 0); + } + + }; +} //namespace + +#endif diff --git a/src/internal/timer.hpp b/src/internal/timer.hpp new file mode 100644 index 0000000..fe46cc6 --- /dev/null +++ b/src/internal/timer.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _TIMER_HPP +#define _TIMER_HPP + +namespace _ldp_timer +{ + template + class Duration : public BaseTimeUnit { + public: + void setNanoTime(std::chrono::nanoseconds nano) { + BaseTimeUnit d = std::chrono::duration_cast(nano); + BaseTimeUnit::operator =(d); + } + + double getSeconds() { + typename BaseTimeUnit::period p; + return 1.0 * BaseTimeUnit::count() * p.num/p.den; + } + + uint64_t getNativeTime() { + return BaseTimeUnit::count(); + } + + friend std::ostream& operator<< (std::ostream& os, + const Duration& d) { + typename BaseTimeUnit::period p; + std::string unit; + switch(p.den/p.num) { + case 1000000000: unit = "ns"; break; + case 1000000: unit = "us"; break; + case 1000: unit = "ms"; break; + case 1: unit = "s"; break; + } + + os << d.count() << " " << unit; + return os; + } + }; + + typedef Duration nanosec; + typedef Duration microsec; + typedef Duration millisec; + typedef Duration sec; + + template + class Timer { + typedef std::chrono::steady_clock CLK; + CLK::time_point tbegin; + _T* const pOT; + public: + Timer(_T* const pTP) + : tbegin(CLK::now()), pOT(pTP) {} + + virtual ~Timer() { + if(pOT) + pOT->setNanoTime(CLK::now() - tbegin); + } + }; +} + +#endif // _TIMER_HPP diff --git a/src/internal/tslog.hpp b/src/internal/tslog.hpp new file mode 100644 index 0000000..f1fdeaa --- /dev/null +++ b/src/internal/tslog.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _TSLOG_HPP +#define _TSLOG_HPP + +#include +#include +#include + +namespace _ldp_tslog +{ + typedef std::ostream& (*t_ManFun)(std::ostream&); + + namespace { + static constexpr bool LOG_ENABLE = false; + static constexpr bool LOG_VERBOSE = false; + } + + const bool get_verbose() { + return LOG_VERBOSE; + } + + const bool get_enable() { + return LOG_ENABLE; + } + + class TsLog + { + private: + static bool m_verbose; + static std::mutex m_mtx; + std::ostream& m_os; + bool m_enable; + + template + TsLog& lckLog(const T& t) { + if(m_enable) { + std::unique_lock lck(m_mtx); + m_os << t; + } + return *this; + } + + public: + TsLog() = delete; + + explicit TsLog(std::ostream& os, bool enable = true) + : m_os(os), m_enable(enable) {} + + virtual ~TsLog() {} + + template + TsLog& operator<< (const T& t) { + return lckLog(t); + } + + TsLog& operator<< (t_ManFun f) { + return lckLog(f); + } + + }; + std::mutex TsLog::m_mtx; +} + +namespace { + //Thread-safe loggers + _ldp_tslog::TsLog tout(std::cout, _ldp_tslog::get_enable()); + _ldp_tslog::TsLog terr(std::cerr, _ldp_tslog::get_enable()); + + namespace verbose { + _ldp_tslog::TsLog tout(std::cout, _ldp_tslog::get_enable() && _ldp_tslog::get_verbose()); + _ldp_tslog::TsLog terr(std::cerr, _ldp_tslog::get_enable() && _ldp_tslog::get_verbose()); + } +} //namespace + +#endif diff --git a/src/internal/xml_parser.hpp b/src/internal/xml_parser.hpp new file mode 100644 index 0000000..8d3eb43 --- /dev/null +++ b/src/internal/xml_parser.hpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _XML_PARSER_HPP +#define _XML_PARSER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timer.hpp" +#include "xml_policy.hpp" + +namespace _ldp_xml_parser +{ + class XmlAsyncParser : boost::noncopyable + { + public: + XmlAsyncParser() { + } + + virtual ~XmlAsyncParser() { + } + + ErrCode parse_policy(const std::string bus, const std::string fname, const std::chrono::milliseconds timeout) { + set_policy_bus_filename(bus, fname); + m_xml_policy.init(); + ErrCode err = parse(timeout); + return err; + } + + ErrCode can_send(const std::string bus, + const std::string user, + const std::string group, + const std::string label, + const std::string destination, + const std::string path, + const std::string interface, + const std::string member, + const std::string type) { + std::vector idx_v = { user, group, destination, path, interface, member, type }; + return m_xml_policy.can_send_to(bus, idx_v, label); + } + + ErrCode can_recv(const std::string bus, + const std::string user, + const std::string group, + const std::string label, + const std::string sender, + const std::string path, + const std::string interface, + const std::string member, + const std::string type) { + std::vector idx_v = { user, group, sender, path, interface, member, type }; + return m_xml_policy.can_recv_from(bus, idx_v, label); + } + + ErrCode can_own(const std::string bus, + const std::string user, + const std::string group, + const std::string service) { + std::vector idx_v = { user, group, service }; + return m_xml_policy.can_own_what(bus, idx_v); + } + + + private: + //IO operation + std::string m_bus; + std::string m_filename; + static std::map m_hashes; + static std::mutex m_io_xml_mtx; + + //Data obtained from XML + static XmlPolicy m_xml_policy; + + //Called by calling user thread + void set_policy_bus_filename(const std::string& bus, const std::string& fname) { + m_filename = fname; + m_bus = bus; + } + + const std::string& get_policy_bus() const { + return m_bus; + } + + const std::string& get_policy_filename() const { + return m_filename; + } + + ErrCode parse(const std::chrono::milliseconds timeout) { + ErrCode err; + std::vector incl_files; + + err = parse(get_policy_filename(), incl_files, timeout); + if(err.is_ok()) { + for(const auto& x : incl_files) { + err = parse(x, incl_files, timeout); + if(err.is_error()) { break; } + } + } + + if(err.is_ok()) { + m_xml_policy.print_decision_trees(); + } + + return err; + } + + ErrCode parse(const std::string& filename, std::vector& included_files, const std::chrono::milliseconds timeout) { + std::pair errparam; + + verbose::tout << "=== XML PARSING BEGIN === : " << filename << std::endl; + + auto fut = std::async(std::launch::async, &XmlAsyncParser::async_xml_parse, this, filename); + + auto r = fut.wait_for(timeout); + if(r == std::future_status::ready) { + errparam = fut.get(); + if(errparam.first.get() >= 0) { + get_included_files(filename, errparam.second, included_files); + } + } else if(r == std::future_status::timeout) { + errparam.first = ErrCode::timeout("XML parsing timeout"); + } + + verbose::tout << "=== XML PARSING END ===" << std::endl << std::endl; + tout << "Processing of " << filename << " -> [" << errparam.first.get() << ", " << errparam.first.get_str() << "]" << std::endl; + return errparam.first; + } + + //Get all the .conf files within included subdirectory, POSIX style as boost::filesystem is not header-only + void get_included_files(const std::string& filename, const std::string& incldir, std::vector& files) { + if(get_policy_filename() == filename && incldir != "") { + DIR *dir; + struct dirent *ent; + std::string fname; + std::copy(filename.begin(), filename.end(), fname.begin()); + std::string dname = dirname(const_cast(fname.c_str())); + dname += (std::string("/") + incldir); + files.clear(); + if((dir = opendir(dname.c_str())) != NULL) { + while((ent = readdir(dir)) != NULL) { + std::string s(ent->d_name); + if(s.find(".conf") != std::string::npos) { + files.push_back(dname + std::string("/") + s); + } + } + closedir(dir); + + tout << std::endl << "includedir for " << filename << " is " << incldir << ", " << files.size() << " included files found:" << std::endl; + if(_ldp_tslog::get_enable()) { std::copy(files.begin(), files.end(), std::ostream_iterator(std::cout, "\n")); } + tout << std::endl; + } else { + terr << "could not open directory " << dname << std::endl; + } + } + } + + //All 'async_*' methods are executed in library's internal worker threads + std::pair async_xml_parse(const std::string& filename) { + std::pair ret; + _ldp_timer::microsec latency; + + try { + boost::property_tree::ptree pt; + + //XML file IO critical section + { + std::unique_lock lck(m_io_xml_mtx); + _ldp_timer::Timer<_ldp_timer::microsec> t(&latency); + + std::size_t hash; + if(async_xml_parsing_needed(filename, hash)) { + read_xml(filename, pt); + async_xml_hash_update(filename, hash); + } + } + + m_xml_policy.update(get_policy_bus(), pt); + + ret.second = pt.get("busconfig.includedir", ""); + + ret.first = ErrCode::ok(); + } catch(const boost::property_tree::xml_parser::xml_parser_error& ex) { + ret.first = ErrCode::error(ex.what()); + } catch(const boost::property_tree::ptree_error& ex) { + ret.first = ErrCode::error(ex.what()); + } catch(...) { + ret.first = ErrCode::error(filename + std::string(": unknown error while parsing XML")); + } + + tout << "XML processing latency: " << latency << std::endl; + return ret; + } + + std::size_t async_xml_hash(const std::string& filename) { + std::size_t seed = 0; + std::ifstream ifs(filename); + for(std::string line; getline(ifs, line); ) { + boost::hash_combine(seed, line); + } + ifs.close(); + + return seed; + } + + void async_xml_hash_update(const std::string& filename, const std::size_t hash) { + auto r = m_hashes.insert(std::pair(filename, hash)); + if(r.second == false) { + auto it = r.first; + it->second = hash; + } + } + + bool async_xml_parsing_needed(const std::string& filename, std::size_t& hash) { + bool ret = false; + hash = async_xml_hash(filename); + auto it = m_hashes.find(filename); + if(it != m_hashes.end()) { + if(hash == it->second) { + ret = false; + } else { + ret = true; + } + } else { + ret = true; + } + return ret; + } + + }; + std::map XmlAsyncParser::m_hashes; + std::mutex XmlAsyncParser::m_io_xml_mtx; + XmlPolicy XmlAsyncParser::m_xml_policy; +} //namespace + +#endif diff --git a/src/internal/xml_policy.hpp b/src/internal/xml_policy.hpp new file mode 100644 index 0000000..f00a879 --- /dev/null +++ b/src/internal/xml_policy.hpp @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _XML_POLICY_HPP +#define _XML_POLICY_HPP + +#include +#include +#include +#include "libdbuspolicy1-private.hpp" +#include "timer.hpp" +#include "tslog.hpp" +#include "cynara.hpp" + +namespace _ldp_xml_parser +{ + namespace { + static const std::string ROOT_FIELD = "busconfig"; + static const std::string ROOT_POLICY = "policy"; + } //namespace + + class XmlPolicy : boost::noncopyable + { + enum class CtxType { DEFAULT, SPECIFIC, MANDATORY }; + + class Key { + public: + static constexpr const char* ANY = "__"; + static constexpr const char* MRY = "!!"; + static constexpr const char* DEF = "??"; + static constexpr const char DELIM = '\\'; + static const size_t IDX_USER = 0; + static const size_t IDX_GROUP = 1; + static const size_t IDX_DEST = 2; + static const size_t IDX_SENDER = IDX_DEST; + static const size_t IDX_SERVICE = IDX_DEST; + static const size_t IDX_PATH = 3; + static const size_t IDX_IFACE = 4; + static const size_t IDX_MEMBER = 5; + static const size_t IDX_TYPE = 6; + + static const size_t IDX_TOTAL_LENGTH = IDX_TYPE + 1; + static const size_t IDX_OWN_LENGTH = IDX_SERVICE + 1; + static const size_t IDX_DEFAULT = IDX_GROUP + 1; + + std::string m_bus; + std::vector m_path_content; + std::string m_privilege; + bool m_bsend; + bool m_brecv; + bool m_bown; + bool m_bcheck; + bool m_ballow; + static size_t m_weight; + + Key(const std::string& bus) + : m_bus(bus), + m_path_content(std::vector(IDX_TOTAL_LENGTH, ANY)), + m_bsend(false), + m_brecv(false), + m_bown(false), + m_bcheck(false), + m_ballow(false) {} + + ~Key() {} + + void reset_attributes() { + m_bsend = m_brecv = m_bown = m_bcheck = m_ballow = false; + std::fill(m_path_content.begin() + IDX_DEFAULT, m_path_content.end(), ANY); + } + + const std::string get_path() const { + std::string path = "R"; + auto it_cend = m_bown ? m_path_content.cbegin() + IDX_OWN_LENGTH : m_path_content.cend(); + for(auto it = m_path_content.cbegin(); it != it_cend; ++it) { + path += (std::string(1, Key::DELIM) + *it); + } + return path; + } + }; + + class Leaf { + bool m_decision; + bool m_check; + std::string m_privilege; + size_t m_weight; + + public: + Leaf() : m_decision(false), m_check(false), m_privilege(""), m_weight(0) {}; + + Leaf(bool decision, bool check, const std::string& privilege, size_t weight) + : m_decision(decision), m_check(check), m_privilege(privilege), m_weight(weight) {} + + bool get_decision() const { + return m_decision; + } + + bool get_check() const { + return m_check; + } + + const std::string& get_privilege() const { + return m_privilege; + } + + size_t get_weight() const { + return m_weight; + } + + friend std::ostream& operator<<(std::ostream& os, const Leaf& lf) { + if(lf.m_check) { + os << "check," << lf.m_privilege << "," << lf.m_weight; + } else { + os << (lf.m_decision ? "true" : "false") << "," << lf.m_weight; + } + return os; + } + + friend std::istream& operator>>(std::istream& is, Leaf& lf) { + std::string s; + is >> s; + boost::char_separator sep(","); + boost::tokenizer> tokens(s, sep); + const auto size = std::distance(tokens.begin(), tokens.end()); + for(auto it = tokens.begin(); it != tokens.end(); ++it) { + const auto it_last = std::next(tokens.begin(), size - 1); + if(it == tokens.begin()) { + if(size > 2) { + lf.m_check = (*it == "check") ? true : false; + } else { + lf.m_decision = (*it == "true") ? true : false; + } + } else if(it == it_last) { + lf.m_weight = std::stoul(*it); + } else { + if(size > 2) { + lf.m_privilege = *it; + } + } + } + return is; + } + }; + + static const std::string get_context_str(const CtxType& ctx_type) { + switch(ctx_type) { + case CtxType::DEFAULT: return "(default)"; break; + case CtxType::SPECIFIC: return "(specific)"; break; + case CtxType::MANDATORY: return "(mandatory)"; break; + default: return ""; break; + } + } + + static const std::string get_field_str(const std::string& field) { + return (field == "") ? Key::ANY : field; + } + + //Data obtained from XML parsing - decision trees + typedef std::map Trees_t; + std::map m_dec_trees; + std::mutex m_xml_policy_mtx; + + boost::property_tree::ptree* get_decision_tree(const std::string& bus, const std::string& tree_type) { + boost::property_tree::ptree* p_tree = NULL; + + auto it1 = m_dec_trees.find(bus); + if(it1 != m_dec_trees.end()) { + auto it2 = it1->second.find(tree_type); + if(it2 != it1->second.end()) { + p_tree = &it2->second; + } + } + return p_tree; + } + + boost::property_tree::ptree* get_decision_tree(const Key& key) { + std::string tree_type; + if(key.m_bsend) { tree_type = "SEND"; } + else if(key.m_brecv) { tree_type = "RECV"; } + else if(key.m_bown) { tree_type = "OWN"; } + + return get_decision_tree(key.m_bus, tree_type); + } + + void print_decision_tree(const boost::property_tree::ptree& pt, int level = 0) { + for(const auto& v : pt) { + print_field(v, level); + print_decision_tree(v.second, level + 1); + } + } + + void print_decision_key(const Key& key) { + if(_ldp_tslog::get_verbose()) { + std::string s = key.m_bus + " "; + if(key.m_bsend && !key.m_brecv) { s += "--> #"; } + if(!key.m_bsend && key.m_brecv) { s += "<-- #"; } + if(!key.m_bsend && !key.m_brecv && key.m_bown) { s += "OWN #"; } + std::string prv = key.m_bcheck ? key.m_privilege : ""; + verbose::tout << s + << (key.m_bcheck ? "check " : std::to_string(key.m_ballow)) + << prv + << " : " + << key.get_path() + << " (weight: " + << key.m_weight + << ")" + << std::endl; + } + } + + void update_decision_tree(const Key& key) { + if(!key.get_path().empty()) { + print_decision_key(key); + + //update + boost::property_tree::ptree* const p_tree = get_decision_tree(key); + if(p_tree) { + boost::property_tree::ptree::path_type tpath(key.get_path(), Key::DELIM); + p_tree->put(tpath, Leaf(key.m_ballow, key.m_bcheck, key.m_privilege, key.m_weight)); + } + } + } + + void update_decision_path(const boost::property_tree::ptree::value_type& v, + Key& key, + CtxType& current_ctx, + bool& allden, + bool& bcheck, + bool& attr) { + if(v.first == "allow") { + allden = true; + bcheck = false; + attr = false; + key.reset_attributes(); + } else if(v.first == "deny") { + allden = false; + bcheck = false; + attr = false; + key.reset_attributes(); + } else if(v.first == "check") { + allden = false; + bcheck = true; + attr = false; + key.reset_attributes(); + } else if(v.first == "") { + attr = true; + ++key.m_weight; + } else { + if(attr) { + std::string data_str = (v.second.data() == "*") ? Key::ANY : v.second.data(); + if(v.first == "context") { + if(data_str == "mandatory") { + key.m_path_content[Key::IDX_USER] = Key::MRY; + key.m_path_content[Key::IDX_GROUP] = Key::MRY; + current_ctx = CtxType::MANDATORY; + } else if(data_str == "default") { + key.m_path_content[Key::IDX_USER] = Key::DEF; + key.m_path_content[Key::IDX_GROUP] = Key::DEF; + current_ctx = CtxType::DEFAULT; + } + } else if(v.first == "user") { + if(current_ctx == CtxType::SPECIFIC) { + key.m_path_content[Key::IDX_USER] = data_str; + } + } else if(v.first == "group") { + if(current_ctx == CtxType::SPECIFIC) { + key.m_path_content[Key::IDX_GROUP] = data_str; + } + } else { + if(field_has(v, "send_")) { + key.m_bsend = true; + } + if(field_has(v, "receive_")) { + key.m_brecv = true; + } + if(v.first == "own") { + key.m_bown = true; + key.m_path_content[Key::IDX_SERVICE] = data_str; + } + if(field_has(v, "_destination")) { + key.m_path_content[Key::IDX_DEST] = data_str; + } + if(field_has(v, "_sender")) { + key.m_path_content[Key::IDX_SENDER] = data_str; + } + if(field_has(v, "_path")) { + key.m_path_content[Key::IDX_PATH] = data_str; + } + if(field_has(v, "_interface")) { + key.m_path_content[Key::IDX_IFACE] = data_str; + } + if(field_has(v, "_member")) { + key.m_path_content[Key::IDX_MEMBER] = data_str; + } + if(field_has(v, "_type")) { + key.m_path_content[Key::IDX_TYPE] = data_str; + } + if(v.first == "privilege") { + key.m_privilege = data_str; + } + + key.m_bcheck = bcheck; + key.m_ballow = allden; + } + } + } + } + + bool field_has(const boost::property_tree::ptree::value_type& v, const std::string& substr) { + return (v.first.find(substr) != std::string::npos); + } + + void print_field(const boost::property_tree::ptree::value_type& v, int level) { + verbose::tout << ((level > 0) ? std::string((level - 1) * 8, ' ') + std::string(8, '.') : "") + << v.first + << " : " + << v.second.data() + << std::endl; + } + + void xml_traversal(const boost::property_tree::ptree& pt, + Key& key, + CtxType& current_ctx, + bool allden = false, + bool bcheck = false, + bool attr = false, + int level = 0) { + static const int Q_XML_MAX_LEVEL = 10; + + if(level < Q_XML_MAX_LEVEL) { + for(const auto& v : pt) { + if(v.first == "") { continue; } + + update_decision_path(v, key, current_ctx, allden, bcheck, attr); + //print_field(v, level); + xml_traversal(v.second, key, current_ctx, allden, bcheck, attr, level + 1); + } + + if(!pt.empty() && attr && level > 1) { + update_decision_tree(key); + } + } else { + terr << "XML traversal max level reached: " << level << std::endl; + } + } + + void print_indexing_path(size_t idx, const std::string& path, const Leaf& leaf = Leaf(), bool empty = true) { + if(_ldp_tslog::get_verbose()) { + std::string s; + if(!empty) { + s = " : <"; + s += (leaf.get_check() + ? std::string("check: ") + std::to_string(leaf.get_check()) + ", privilege: " + leaf.get_privilege() + : std::string("decision: ") + std::to_string(leaf.get_decision())); + s += (std::string(", weight: ") + std::to_string(leaf.get_weight())); + s += std::string(">"); + } + + verbose::tout << "path #" + << idx + << " : " + << path + << s + << std::endl; + } + } + + void prepare_indexing_path(const std::vector& idx_v, + const size_t pattern, + const size_t obfuscate_order, + const CtxType& ctx_type, + std::string& path) { + + const size_t offset = Key::IDX_DEFAULT; + path = "R"; + + if(ctx_type == CtxType::SPECIFIC) { + switch(obfuscate_order) { + case 0: + path += (std::string(1, Key::DELIM) + get_field_str(idx_v[Key::IDX_USER])); + path += (std::string(1, Key::DELIM) + Key::ANY); + break; + case 1: + path += (std::string(1, Key::DELIM) + Key::ANY); + path += (std::string(1, Key::DELIM) + get_field_str(idx_v[Key::IDX_GROUP])); + break; + case 2: + path += (std::string(1, Key::DELIM) + get_field_str(idx_v[Key::IDX_USER])); + path += (std::string(1, Key::DELIM) + get_field_str(idx_v[Key::IDX_GROUP])); + break; + case 3: + default: + path += (std::string(1, Key::DELIM) + Key::ANY); + path += (std::string(1, Key::DELIM) + Key::ANY); + break; + } + } else { + for(size_t i = 0; i < offset; ++i) { + const std::string as = (ctx_type == CtxType::MANDATORY) ? Key::MRY : Key::DEF; + path += (std::string(1, Key::DELIM) + as); + } + } + + const size_t m = 1; + const size_t n = idx_v.size() - offset; + for(size_t i = 0; i < n; ++i) { + std::string s = get_field_str(idx_v[i + offset]); + path += Key::DELIM; + if(pattern & (m << i)) { + path += Key::ANY; + } else { + path += s; + } + } + } + + ErrCode index_decision_tree(const boost::property_tree::ptree& pt, + const std::vector& idx_v, + const std::string& label, + const CtxType& ctx_type) { + ErrCode err; + bool found = false; + size_t weight = 0; + const size_t offset = Key::IDX_DEFAULT; + const size_t m = (ctx_type == CtxType::SPECIFIC) ? (1 << offset) : 1; + for(size_t ob_or = 0; ob_or < m; ++ob_or) { + Leaf leaf_found; + const size_t n = 1 << (idx_v.size() - offset); + for(size_t p = 0; p < n; ++p) { + std::string path; + try { + prepare_indexing_path(idx_v, p, ob_or, ctx_type, path); + boost::property_tree::ptree::path_type tpath(path, Key::DELIM); + + auto dec = pt.get(tpath); + + print_indexing_path(p, path, dec, false); + found = true; + if(dec.get_weight() >= weight) { + weight = dec.get_weight(); + leaf_found = dec; + } + } catch(const boost::property_tree::ptree_error& ex) { + //Path doesn't exist, continue + print_indexing_path(p, path); + if(!found) { err = ErrCode::error("No path"); } + } catch(...) { + print_indexing_path(p, path); + verbose::tout << "Unknown exception while indexing decision tree!" << std::endl; + if(!found) { err = ErrCode::error("Unknown err, no path"); } + } + } + + if(found) { + if(leaf_found.get_check()) { + verbose::tout << __func__ + << ": cynara check needed for privilege " << leaf_found.get_privilege() + << ", weight " << leaf_found.get_weight() + << std::endl; + + //cynara check + try { + bool br = _ldp_cynara::Cynara::check(label, leaf_found.get_privilege(), idx_v[Key::IDX_USER]); + err = ErrCode::ok(br); + } catch(const std::runtime_error& ex) { + err = ErrCode::error(ex.what()); + } + } else { + err = ErrCode::ok(leaf_found.get_decision()); + } + + verbose::tout << __func__ << ": returning decision #" << err.get() << " " << err.get_str() << ", weight " << leaf_found.get_weight() << std::endl; + break; + } + } + + return err; + } + + ErrCode index_decision_tree_lat(const boost::property_tree::ptree& pt, + const std::vector& idx_v, + const std::string& label, + const CtxType& ctx_type) { + ErrCode err; + + tout << "context: " << get_context_str(ctx_type) << ", indexing arguments: "; + if(_ldp_tslog::get_enable()) { std::copy(idx_v.begin(), idx_v.end(), std::ostream_iterator(std::cout, ", ")); } + tout << std::endl; + + //Examine policy data and make decision + _ldp_timer::microsec latency; + { + _ldp_timer::Timer<_ldp_timer::microsec> t(&latency); + err = index_decision_tree(pt, idx_v, label, ctx_type); + } + + tout << __func__ << ": #" << err.get() << " " << err.get_str() << " " << get_context_str(ctx_type) << std::endl; + tout << "tree indexing latency: " << latency << std::endl; + return err; + } + + ErrCode can_do_action(const std::string& bus, const std::string& tree_type, const std::vector& idx_v, const std::string& label = "") { + std::unique_lock lck(m_xml_policy_mtx); + ErrCode err; + boost::property_tree::ptree* const p_tree = get_decision_tree(bus, tree_type); + if(p_tree) { + err = index_decision_tree_lat(*p_tree, idx_v, label, CtxType::MANDATORY); + if(!err.is_ok()) { + err = index_decision_tree_lat(*p_tree, idx_v, label, CtxType::SPECIFIC); + if(!err.is_ok()) { + err = index_decision_tree_lat(*p_tree, idx_v, label, CtxType::DEFAULT); + } + } + } else { + err = ErrCode::error("Get decision tree returned NULL ptr"); + } + tout << __func__ << ": #" << err.get() << " " << err.get_str() << std::endl; + return err; + } + + public: + XmlPolicy() { + Trees_t t; + t.emplace("SEND", typename Trees_t::mapped_type()); + t.emplace("RECV", typename Trees_t::mapped_type()); + t.emplace("OWN", typename Trees_t::mapped_type()); + m_dec_trees.emplace("SYSTEM", t); + m_dec_trees.emplace("SESSION", t); + } + + virtual ~XmlPolicy() {} + + void init() { + std::unique_lock lck(m_xml_policy_mtx); + Key::m_weight = 0; + } + + void update(const std::string& bus, const boost::property_tree::ptree& pt) { + if(!pt.empty()) { + std::unique_lock lck(m_xml_policy_mtx); + const auto& children = pt.get_child(ROOT_FIELD); + for(const auto& x : children) { + if(x.first == ROOT_POLICY) { + Key key(bus); + CtxType current_ctx = CtxType::SPECIFIC; + xml_traversal(x.second, key, current_ctx); + } + } + } + } + + ErrCode can_send_to(const std::string bus, const std::vector& idx_v, const std::string label) { + return can_do_action(bus, "SEND", idx_v, label); + } + + ErrCode can_recv_from(const std::string bus, const std::vector& idx_v, const std::string label) { + return can_do_action(bus, "RECV", idx_v, label); + } + + ErrCode can_own_what(const std::string bus, const std::vector& idx_v) { + return can_do_action(bus, "OWN", idx_v); + } + + void print_decision_trees() { + if(_ldp_tslog::get_verbose()) { + std::unique_lock lck(m_xml_policy_mtx); + + for(const auto& x : m_dec_trees) { + for(const auto& y : x.second) { + verbose::tout << x.first << " " << y.first << " " << (y.second.empty() ? "(empty)" : "") << std::endl; + print_decision_tree(y.second); + } + } + } + } + + }; //XmlPolicy + size_t XmlPolicy::Key::m_weight = 0; +} //namespace + +#endif diff --git a/src/libdbuspolicy1-private.h b/src/libdbuspolicy1-private.h new file mode 100644 index 0000000..58e27bd --- /dev/null +++ b/src/libdbuspolicy1-private.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#ifndef _LIBDBUSPOLICY1_PRIVATE_H_ +#define _LIBDBUSPOLICY1_PRIVATE_H_ + +#include +#include + +#include +#include "kdbus.h" + + +#define DBUSPOLICY1_EXPORT __attribute__ ((visibility("default"))) + +#endif diff --git a/src/libdbuspolicy1.c b/src/libdbuspolicy1.c new file mode 100644 index 0000000..01d586a --- /dev/null +++ b/src/libdbuspolicy1.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2015 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. + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libdbuspolicy1-private.h" +#include "internal/internal.h" + +#define KDBUS_SYSTEM_BUS_PATH "/sys/fs/kdbus/0-system/bus" +#define KDBUS_POOL_SIZE (16 * 1024UL * 1024UL) + +#define ALIGN8(l) (((l) + 7) & ~7) +#define UID_INVALID ((uid_t) -1) +#define GID_INVALID ((gid_t) -1) + +#define FOREACH_STRV(i,l, s, os)\ +for(\ +({\ +i=0;\ +l = strlen(s);\ +name = malloc(sizeof(char*)*(l+1));\ +strcpy(os, s);\ +});\ +i < l;\ +i++) + +#define GET_NEXT_STR(i,s,os)\ + os = s+i;\ + for(;s[i] && s[i] != ' ';i++);\ + s[i] = 0; + + + +/** A process ID */ +typedef unsigned long dbus_pid_t; +/** A user ID */ +typedef unsigned long dbus_uid_t; +/** A group ID */ +typedef unsigned long dbus_gid_t; + +struct kcreds { + uid_t uid; + gid_t gid; + char* label; + char** names; +}; + +struct kconn { + int fd; + uint64_t id; + char *pool; +}; +struct udesc { + unsigned int bus_type; + char user[256]; + dbus_uid_t uid; + char group[256]; + dbus_gid_t gid; + char label[256]; + struct kconn* conn; +}; + +static void print_udesc(const char* const entry, const struct udesc* const p_udesc) { +} + +static int kdbus_open_system_bus(void) +{ + return open(KDBUS_SYSTEM_BUS_PATH, O_RDWR|O_NOCTTY|O_LARGEFILE|O_CLOEXEC ); +} + +static int kdbus_hello(struct kconn *kc, uint64_t hello_flags, uint64_t attach_flags_send, uint64_t attach_flags_recv) +{ + struct kdbus_cmd_hello kcmd_hello; + int r; + + memset(&kcmd_hello, 0, sizeof(kcmd_hello)); + kcmd_hello.flags = hello_flags; + kcmd_hello.attach_flags_send = attach_flags_send; + kcmd_hello.attach_flags_recv = attach_flags_recv; + kcmd_hello.size = sizeof(kcmd_hello); + kcmd_hello.pool_size = KDBUS_POOL_SIZE; + + r = ioctl(kc->fd, KDBUS_CMD_HELLO, &kcmd_hello); + if (r < 0) + return -errno; + + kc->id = (uint64_t)kcmd_hello.id; + kc->pool = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, kc->fd, 0); + if (kc->pool == MAP_FAILED) + return -errno; + + return 0; +} + +static int kdbus_get_creds_from_name(struct kconn* kc, struct kcreds* kcr, const char* name) +{ + struct kdbus_cmd_info* cmd; + struct kdbus_info* conn_info; + struct kdbus_item *item; + char** tmp_names; + int j,size,r,l,counter; + + counter = 0; + kcr->names = calloc(counter+1, sizeof(char *)); + + kcr->uid = UID_INVALID; + kcr->gid = GID_INVALID; + kcr->label = NULL; + + + l = strlen(name) + 1; + + size = offsetof(struct kdbus_cmd_info, items) + ALIGN8((l) + offsetof(struct kdbus_item, data));//ietms+aligned(l+ITEM_HEADER_SIZE) + cmd = aligned_alloc(8, size); + memset(cmd, 0, sizeof(struct kdbus_cmd_info)); + cmd->items[0].size = l + offsetof(struct kdbus_item, data) ; + cmd->items[0].type = KDBUS_ITEM_NAME; + memcpy(cmd->items[0].str, name, l); + cmd->size = size; + cmd->attach_flags = KDBUS_ATTACH_CREDS | KDBUS_ATTACH_SECLABEL | KDBUS_ATTACH_NAMES ; + + r = ioctl(kc->fd, KDBUS_CMD_CONN_INFO, cmd); + if (r < 0) + return -errno; + + conn_info = (struct kdbus_info *) ((uint8_t *) kc->pool + cmd->offset); + + for(item = conn_info->items; + ((uint8_t *)(item) < (uint8_t *)(conn_info) + (conn_info)->size) && + ((uint8_t *) item >= (uint8_t *) conn_info); + item = ((typeof(item))(((uint8_t *)item) + ALIGN8((item)->size))) ) + { + switch (item->type) + { + case KDBUS_ITEM_CREDS: + if (item->creds.euid != UID_INVALID) + { + kcr->uid = (uid_t) item->creds.euid; + } + if (item->creds.egid != GID_INVALID) + { + kcr->gid = (gid_t) item->creds.egid; + } + break; + case KDBUS_ITEM_SECLABEL: + kcr->label = strdup(item->str); + break; + case KDBUS_ITEM_OWNED_NAME: + counter++; + tmp_names = calloc(counter+1, sizeof(char*)); + for (j = 0;kcr->names[j]; j++) + { + tmp_names[j] = kcr->names[j]; + } + tmp_names[j] = strdup(item->name.name); + free(kcr->names); + kcr->names = tmp_names; + break; + } + } + + return 0; +} + +static void kcreds_free(struct kcreds* kcr) +{ + int i = 0; + if (kcr == NULL) + return; + + free(kcr->label); + for (i=0; kcr->names[i];i++) + free(kcr->names[i]); + free(kcr->names[i]); + free(kcr->names); + free(kcr); +} +/** + * dbuspolicy1_init + * @config_name: name of the XML configuration file + * + * Set the configuration file used by the calling application + **/ +DBUSPOLICY1_EXPORT void* dbuspolicy1_init(unsigned int bus_type) +{ + struct kconn* kc; + uint64_t hello_flags = 0; + uint64_t attach_flags_send = _KDBUS_ATTACH_ANY; + uint64_t attach_flags_recv = _KDBUS_ATTACH_ALL; + int r,fdl; + struct udesc* p_udesc; + + kc = (struct kconn*) calloc(1, sizeof(struct kconn)); + if (!kc) + return NULL; + + kc->fd = kdbus_open_system_bus(); + r = kdbus_hello(kc, hello_flags, attach_flags_send, attach_flags_recv); + if (r < 0) { + free(kc); + return NULL; + } + + r = __internal_init(bus_type, (bus_type == SYSTEM_BUS) ? SYSTEM_BUS_CONF_FILE : SESSION_BUS_CONF_FILE); + if(r >= 0) { + p_udesc = (struct udesc*)malloc(sizeof(struct udesc)); + if(p_udesc) { + p_udesc->bus_type = bus_type; + p_udesc->uid = getuid(); + p_udesc->gid = getgid(); + struct passwd* pwd = getpwuid(p_udesc->uid); + strcpy(p_udesc->user, pwd->pw_name); + struct group* gg = getgrgid(p_udesc->gid); + strcpy(p_udesc->group, gg->gr_name); + p_udesc->conn = kc; + + fdl = open("/proc/self/attr/current", 0, S_IRUSR); + if (fdl < 0) + { + fprintf(stderr,"Cannot open /proc/self/attr/current\n"); + dbuspolicy1_free(p_udesc); + return NULL; + } + + r = read(fdl, p_udesc->label, 256); + if (r < 0) + { + fprintf(stderr, "Cannot read from /proc/self/attr/current\n"); + close(fdl); + dbuspolicy1_free(p_udesc); + return NULL; + } + close(fdl); + + + print_udesc("New configuration", p_udesc); + } + } else { + p_udesc = NULL; + } + return p_udesc; +} + +DBUSPOLICY1_EXPORT void dbuspolicy1_free(void* configuration) +{ + struct udesc* p_udesc = (struct udesc*)configuration; + if(p_udesc) { + print_udesc("Freeing configuration", p_udesc); + free(p_udesc->conn); + free(p_udesc); + p_udesc = NULL; + } +} + +/** + * dbuspolicy1_can_send + * @param: <> + * @return: <> + * + * Description. + **/ +DBUSPOLICY1_EXPORT int dbuspolicy1_check_out(void* configuration, + const char *destination, + const char *sender, + const char *path, + const char *interface, + const char *member, + int message_type, + const char *error_name, + int reply_serial, + int requested_reply) +{ + struct udesc* const p_udesc = (struct udesc*)configuration; + int i, rs, rr, l, r = 0; + struct kcreds* p_creds = NULL; + char gid[25], uid[25]; + char* name = NULL; + char empty_names = 1; + + rs = 0; + rr = 1; + + if (message_type != DBUSPOLICY_MESSAGE_TYPE_SIGNAL || (destination != NULL && *destination != '\0') ) { + p_creds = calloc(1, sizeof(struct kcreds)); + r = kdbus_get_creds_from_name(p_udesc->conn, p_creds, destination); + if(r < 0) { + kcreds_free(p_creds); + return 0; + } + + snprintf(uid, 24, "%lu", (unsigned long int)p_creds->uid); + snprintf(gid, 24, "%lu", (unsigned long int)p_creds->gid); + if (!p_creds->names[0]) + empty_names = 0; + + for (i=0;p_creds->names[i];i++) + { + rs = __internal_can_send(p_udesc->bus_type, p_udesc->user, p_udesc->group, p_udesc->label, p_creds->names[i], path, interface, member, message_type); + if (rs > 0) + break; + } + } + + if (empty_names) + rs = __internal_can_send(p_udesc->bus_type, p_udesc->user, p_udesc->group, p_udesc->label, destination, path, interface, member, message_type); + + if (message_type != DBUSPOLICY_MESSAGE_TYPE_SIGNAL) { + rr = 0; + + if (!sender) + rr = __internal_can_recv(p_udesc->bus_type, uid, gid, p_creds->label, sender, path, interface, member, message_type); + else + FOREACH_STRV(i, l, sender, name) { + char* source; + GET_NEXT_STR(i, name, source); + rr = __internal_can_recv(p_udesc->bus_type, uid, gid, p_creds->label, source, path, interface, member, message_type); + if (rr > 0) + break; + } + } + + free(name); + kcreds_free(p_creds); + + if(rs > 0 && rr > 0) { r = 1; } + if(rs < 0 || rr < 0) { r = -1; } + return r; +} +/** + * dbuspolicy1_can_send + * @param: <> + * @return: <> + * + * Description. + **/ +DBUSPOLICY1_EXPORT int dbuspolicy1_check_in(void* configuration, + const char *destination, + const char *sender, + const char *sender_label, + uid_t sender_uid, + gid_t sender_gid, + const char *path, + const char *interface, + const char *member, + int message_type, + const char *error_name, + int reply_serial, + int requested_reply) +{ + struct udesc* const p_udesc = (struct udesc*)configuration; + int i, rs, rr, l, r = 0; + struct kcreds* p_creds = NULL; + char gid[25], uid[25]; + char* name = NULL; + + rs = 0; + rr = 1; + + snprintf(uid, 24, "%lu", (unsigned long int)sender_uid); + snprintf(gid, 24, "%lu", (unsigned long int)sender_gid); + + if (!destination) + rs = __internal_can_send(p_udesc->bus_type, uid, gid, sender_label, destination, path, interface, member, message_type); + else + FOREACH_STRV(i, l, destination, name) { + char* dest; + GET_NEXT_STR(i, name, dest); + + rs = __internal_can_send(p_udesc->bus_type, uid, gid, sender_label, dest, path, interface, member, message_type); + if (rs > 0) + break; + } + free(name); + + if(message_type != DBUSPOLICY_MESSAGE_TYPE_SIGNAL) { + rr = 0; + + if (!sender) + rr = __internal_can_recv(p_udesc->bus_type, p_udesc->user, p_udesc->group, p_udesc->label, sender, path, interface, member, message_type); + else + FOREACH_STRV(i, l, sender, name) { + char* source; + GET_NEXT_STR(i, name, source); + rr = __internal_can_recv(p_udesc->bus_type, p_udesc->user, p_udesc->group, p_udesc->label, source, path, interface, member, message_type); + if(rr > 0) + break; + } + free(name); + } + kcreds_free(p_creds); + + if(rs > 0 && rr > 0) { r = 1; } + if(rs < 0 || rr < 0) { r = -1; } + return r; +} + + +/** + * dbuspolicy1_can_send + * @param: <> + * @return: <> + * + * Description. + **/ +DBUSPOLICY1_EXPORT int dbuspolicy1_can_own(void* configuration, const char* const service) +{ + struct udesc* const p_udesc = (struct udesc*)configuration; + return __internal_can_own(p_udesc->bus_type, p_udesc->user, p_udesc->group, service); +} diff --git a/src/libdbuspolicy1.pc.in b/src/libdbuspolicy1.pc.in new file mode 100644 index 0000000..2e04c48 --- /dev/null +++ b/src/libdbuspolicy1.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libdbuspolicy1 +Description: Library for something with dbuspolicy1 +Version: @VERSION@ +Libs: -L${libdir} -ldbuspolicy1 +Libs.private: +Cflags: -I${includedir} diff --git a/src/libdbuspolicy1.sym b/src/libdbuspolicy1.sym new file mode 100644 index 0000000..051e64e --- /dev/null +++ b/src/libdbuspolicy1.sym @@ -0,0 +1,19 @@ +LIBDBUSPOLICY1_1 { +global: + dbuspolicy1_init; + dbuspolicy1_free; + dbuspolicy1_check_in; + dbuspolicy1_check_out; + dbuspolicy1_can_own; +local: + *; +}; + +LIBDBUSPOLICY1_3 { +global: + dbuspolicy1_thing_ref; + dbuspolicy1_thing_unref; + dbuspolicy1_thing_get_ctx; + dbuspolicy1_thing_new_from_string; + dbuspolicy1_thing_get_some_list_entry; +} LIBDBUSPOLICY1_1;