[Merge] lp:~thomas-voss/media-hub/introduce-apparmor-interfaces into lp:media-hub

Jim Hodapp jim.hodapp at canonical.com
Mon Dec 8 16:21:45 UTC 2014


Review: Needs Fixing code

One simple comment below.

Diff comments:

> === modified file 'src/core/media/CMakeLists.txt'
> --- src/core/media/CMakeLists.txt	2014-11-26 16:28:50 +0000
> +++ src/core/media/CMakeLists.txt	2014-11-26 16:28:50 +0000
> @@ -91,6 +91,9 @@
>      cover_art_resolver.cpp
>      engine.cpp
>  
> +    apparmor/context.cpp
> +    apparmor/ubuntu.cpp
> +
>      audio/pulse_audio_output_observer.cpp
>      audio/output_observer.cpp
>  
> 
> === added directory 'src/core/media/apparmor'
> === removed file 'src/core/media/apparmor.h'
> --- src/core/media/apparmor.h	2014-09-01 07:49:45 +0000
> +++ src/core/media/apparmor.h	1970-01-01 00:00:00 +0000
> @@ -1,93 +0,0 @@
> -/*
> - * Copyright (C) 2013-2014 Canonical Ltd
> - *
> - * This program is free software: you can redistribute it and/or modify
> - * it under the terms of the GNU Lesser General Public License version 3 as
> - * published by the Free Software Foundation.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> - * GNU Lesser General Public License for more details.
> - *
> - * You should have received a copy of the GNU Lesser General Public License
> - * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> - *
> - * Author: Jim Hodapp <jim.hodapp at canonical.com>
> - */
> -
> -#ifndef APPARMOR_DBUS_H_
> -#define APPARMOR_DBUS_H_
> -
> -#include <core/dbus/macros.h>
> -#include <core/dbus/object.h>
> -#include <core/dbus/service.h>
> -
> -#include <string>
> -#include <chrono>
> -
> -// TODO(tvoss): This really should live in trust-store, providing a straightforward
> -// way for parties involved in managing trust relationships to query peers' apparmor
> -// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
> -// related bug
> -namespace org
> -{
> -namespace freedesktop
> -{
> -namespace dbus
> -{
> -struct DBus
> -{
> -    static const std::string& name()
> -    {
> -        static const std::string s = "org.freedesktop.DBus";
> -        return s;
> -    }
> -
> -    // Gets the AppArmor confinement string associated with the unique connection name. If
> -    // D-Bus is not performing AppArmor mediation, the
> -    // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
> -    DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
> -
> -    struct Stub
> -    {
> -        // Creates a new stub instance for the given object to access
> -        // DBus functionality.
> -        Stub(const core::dbus::Object::Ptr& object) : object{object}
> -        {
> -        }
> -
> -        // Creates a new stub instance for the given bus connection
> -        Stub(const core::dbus::Bus::Ptr& bus)
> -            : object
> -              {
> -                  core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
> -                      ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
> -              }
> -        {
> -        }
> -
> -        // Gets the AppArmor confinement string associated with the unique connection name. If
> -        // D-Bus is not performing AppArmor mediation, the
> -        // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
> -        //
> -        // Invokes the given handler on completion.
> -        void get_connection_app_armor_security_async(
> -                    const std::string& name,
> -                    std::function<void(const std::string&)> handler)
> -        {
> -            object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
> -                        [handler](const core::dbus::Result<std::string>& result)
> -                        {
> -                            if (not result.is_error()) handler(result.value());
> -                        }, name);
> -        }
> -
> -        core::dbus::Object::Ptr object;
> -    };
> -};
> -}
> -}
> -}
> -
> -#endif // APPARMOR_DBUS_H_
> 
> === added file 'src/core/media/apparmor/context.cpp'
> --- src/core/media/apparmor/context.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/media/apparmor/context.cpp	2014-11-26 16:28:50 +0000
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/media/apparmor/context.h>
> +
> +namespace apparmor = core::ubuntu::media::apparmor;
> +
> +apparmor::Context::Context(const std::string& name) : name{name}
> +{
> +    if (name.empty()) throw std::runtime_error
> +    {
> +        "apparmor::Context cannot be created for empty name."
> +    };
> +}
> +
> +const std::string& apparmor::Context::str() const
> +{
> +    return name;
> +}
> 
> === added file 'src/core/media/apparmor/context.h'
> --- src/core/media/apparmor/context.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/apparmor/context.h	2014-11-26 16:28:50 +0000
> @@ -0,0 +1,51 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +#ifndef CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
> +#define CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
> +
> +#include <memory>
> +#include <string>
> +
> +namespace core
> +{
> +namespace ubuntu
> +{
> +namespace media
> +{
> +namespace apparmor
> +{
> +// Models an apparmor context name, and provides convenience functionality
> +// on top of it.
> +class Context
> +{
> +public:
> +    // Constructs a new Context instance for the given raw name.
> +    // Throws std::logic_error for empty names.
> +    explicit Context(const std::string& name);
> +    virtual ~Context() = default;
> +    // Returns the raw string describing the context.
> +    const std::string& str() const;
> +
> +private:
> +    const std::string name;
> +};
> +}
> +}
> +}
> +}
> +#endif // CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
> 
> === added file 'src/core/media/apparmor/dbus.h'
> --- src/core/media/apparmor/dbus.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/apparmor/dbus.h	2014-11-26 16:28:50 +0000
> @@ -0,0 +1,94 @@
> +/*
> + * Copyright (C) 2013-2014 Canonical Ltd
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU Lesser General Public License version 3 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Author: Jim Hodapp <jim.hodapp at canonical.com>
> + */
> +
> +#ifndef CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
> +#define CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
> +
> +#include <core/dbus/bus.h>
> +#include <core/dbus/macros.h>
> +#include <core/dbus/object.h>
> +#include <core/dbus/service.h>
> +
> +#include <string>
> +#include <chrono>
> +
> +// TODO(tvoss): This really should live in trust-store, providing a straightforward
> +// way for parties involved in managing trust relationships to query peers' apparmor
> +// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
> +// related bug
> +namespace org
> +{
> +namespace freedesktop
> +{
> +namespace dbus
> +{
> +struct DBus
> +{
> +    static const std::string& name()
> +    {
> +        static const std::string s = "org.freedesktop.DBus";
> +        return s;
> +    }
> +
> +    // Gets the AppArmor confinement string associated with the unique connection name. If
> +    // D-Bus is not performing AppArmor mediation, the
> +    // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
> +    DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
> +
> +    struct Stub
> +    {
> +        // Creates a new stub instance for the given object to access
> +        // DBus functionality.
> +        Stub(const core::dbus::Object::Ptr& object) : object{object}
> +        {
> +        }
> +
> +        // Creates a new stub instance for the given bus connection
> +        Stub(const core::dbus::Bus::Ptr& bus)
> +            : object
> +              {
> +                  core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
> +                      ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
> +              }
> +        {
> +        }
> +
> +        // Gets the AppArmor confinement string associated with the unique connection name. If
> +        // D-Bus is not performing AppArmor mediation, the
> +        // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
> +        //
> +        // Invokes the given handler on completion.
> +        void get_connection_app_armor_security_async(
> +                    const std::string& name,
> +                    std::function<void(const std::string&)> handler)
> +        {
> +            object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
> +                        [handler](const core::dbus::Result<std::string>& result)
> +                        {
> +                            if (not result.is_error()) handler(result.value());
> +                        }, name);
> +        }
> +
> +        core::dbus::Object::Ptr object;
> +    };
> +};
> +}
> +}
> +}
> +
> +#endif // CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
> 
> === added file 'src/core/media/apparmor/ubuntu.cpp'
> --- src/core/media/apparmor/ubuntu.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/media/apparmor/ubuntu.cpp	2014-11-26 16:28:50 +0000
> @@ -0,0 +1,198 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/media/apparmor/ubuntu.h>
> +
> +#include <core/media/external_services.h>
> +
> +#include <iostream>
> +#include <regex>
> +
> +namespace apparmor = core::ubuntu::media::apparmor;
> +namespace media = core::ubuntu::media;
> +namespace ubuntu = apparmor::ubuntu;
> +
> +namespace
> +{
> +struct Uri
> +{
> +    std::string scheme;
> +    std::string authority;
> +    std::string path;
> +    std::string query;
> +    std::string fragment;
> +};
> +
> +// Poor mans version of a uri parser.
> +// See https://tools.ietf.org/html/rfc3986#appendix-B
> +Uri parse_uri(const std::string& s)
> +{
> +    // Indices into the regex match go here.
> +    struct Index
> +    {
> +        const std::size_t scheme{2};
> +        const std::size_t authority{4};
> +        const std::size_t path{5};
> +        const std::size_t query{7};
> +        const std::size_t fragment{9};
> +    } static index;
> +
> +    static const std::regex regex{R"delim(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)delim"};
> +    std::smatch match;
> +
> +    if (not std::regex_match(s, match, regex)) throw std::runtime_error
> +    {
> +        "Not a valid URI: " + s
> +    };
> +
> +    return Uri
> +    {
> +        match.str(index.scheme),
> +        match.str(index.authority),
> +        match.str(index.path),
> +        match.str(index.query),
> +        match.str(index.fragment)
> +    };
> +}
> +
> +static constexpr std::size_t index_package{1};
> +static constexpr std::size_t index_app{2};
> +
> +// Returns true if the context name is a valid Ubuntu app id.
> +// If it is, out is populated with the package and app name.
> +bool process_context_name(const std::string& s, std::smatch& out)
> +{
> +    // See https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId.
> +    static const std::regex short_re{"(.*)_(.*)"};
> +    static const std::regex full_re{"(.*)_(.*)_(.*)"};
> +
> +    if (std::regex_match(s, out, full_re))
> +        return true;
> +
> +    if (std::regex_match(s, out, short_re))
> +        return true;
> +
> +    return false;
> +}
> +}
> +
> +apparmor::ubuntu::Context::Context(const std::string& name)
> +    : apparmor::Context{name},
> +      unconfined_{str() == ubuntu::unconfined},
> +      has_package_name_{process_context_name(str(), match_)}
> +{
> +    if (not is_unconfined() && not has_package_name()) throw std::logic_error
> +    {
> +        "apparmor::ubuntu::Context: Invalid profile name " + str()
> +    };
> +}
> +
> +bool apparmor::ubuntu::Context::is_unconfined() const
> +{
> +    return unconfined_;
> +}
> +
> +bool apparmor::ubuntu::Context::has_package_name() const
> +{
> +    return has_package_name_;
> +}
> +
> +
> +std::string apparmor::ubuntu::Context::package_name() const
> +{
> +    return std::string{match_[index_package]};
> +}
> +
> +apparmor::ubuntu::DBusDaemonRequestContextResolver::DBusDaemonRequestContextResolver(const core::dbus::Bus::Ptr& bus) : dbus_daemon{bus}
> +{
> +}
> +
> +void apparmor::ubuntu::DBusDaemonRequestContextResolver::resolve_context_for_dbus_name_async(
> +        const std::string& name,
> +        apparmor::ubuntu::RequestContextResolver::ResolveCallback cb)
> +{
> +    dbus_daemon.get_connection_app_armor_security_async(name, [cb](const std::string& context_name)
> +    {
> +        cb(apparmor::ubuntu::Context{context_name});
> +    });
> +}
> +
> +apparmor::ubuntu::RequestAuthenticator::Result apparmor::ubuntu::ExistingAuthenticator::authenticate_open_uri_request(const apparmor::ubuntu::Context& context, const std::string& uri)
> +{
> +    if (context.is_unconfined())
> +        return Result{true, "Client allowed access since it's unconfined"};
> +
> +    Uri parsed_uri = parse_uri(uri);
> +
> +    // All confined apps can access their own files
> +    if (parsed_uri.path.find(std::string(".local/share/" + context.package_name() + "/")) != std::string::npos ||
> +        parsed_uri.path.find(std::string(".cache/" + context.package_name() + "/")) != std::string::npos)
> +    {
> +        return Result
> +        {
> +            true,
> +            "Client can access content in ~/.local/share/" + context.package_name() + " or ~/.cache/" + context.package_name()
> +        };
> +    }
> +    else if (parsed_uri.path.find(std::string("opt/click.ubuntu.com/")) != std::string::npos &&
> +             parsed_uri.path.find(context.package_name()) != std::string::npos)
> +    {
> +        return Result{true, "Client can access content in own opt directory"};
> +    }
> +    else if ((parsed_uri.path.find(std::string("/system/media/audio/ui/")) != std::string::npos ||
> +              parsed_uri.path.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) &&
> +              context.package_name() == "com.ubuntu.camera")
> +    {
> +        return Result{true, "Camera app can access ui sounds"};
> +    }
> +
> +    // TODO: Check if the trust store previously allowed direct access to uri
> +
> +    // Check in ~/Music and ~/Videos
> +    // TODO: when the trust store lands, check it to see if this app can access the dirs and
> +    // then remove the explicit whitelist of the music-app, and gallery-app
> +    else if ((context.package_name() == "com.ubuntu.music" || context.package_name() == "com.ubuntu.gallery") &&
> +            (parsed_uri.path.find(std::string("Music/")) != std::string::npos ||
> +             parsed_uri.path.find(std::string("Videos/")) != std::string::npos ||
> +             parsed_uri.path.find(std::string("/media")) != std::string::npos))
> +    {
> +        return Result{true, "Client can access content in ~/Music or ~/Videos"};
> +    }
> +    else if (parsed_uri.path.find(std::string("/usr/share/sounds")) != std::string::npos)
> +    {
> +        return Result{true, "Client can access content in /usr/share/sounds"};
> +    }
> +    else if (parsed_uri.scheme == "http" || parsed_uri.scheme == "rtsp")
> +    {
> +        return Result{true, "Client can access streaming content"};
> +    }
> +
> +    return Result{false, "Client is not allowed to access: " + uri};
> +}
> +
> +// Returns the platform-default implementation of RequestContextResolver.
> +apparmor::ubuntu::RequestContextResolver::Ptr apparmor::ubuntu::make_platform_default_request_context_resolver(media::helper::ExternalServices& es)
> +{
> +    return std::make_shared<apparmor::ubuntu::DBusDaemonRequestContextResolver>(es.session);
> +}
> +
> +// Returns the platform-default implementation of RequestAuthenticator.
> +apparmor::ubuntu::RequestAuthenticator::Ptr apparmor::ubuntu::make_platform_default_request_authenticator()
> +{
> +    return std::make_shared<apparmor::ubuntu::ExistingAuthenticator>();
> +}
> 
> === added file 'src/core/media/apparmor/ubuntu.h'
> --- src/core/media/apparmor/ubuntu.h	1970-01-01 00:00:00 +0000
> +++ src/core/media/apparmor/ubuntu.h	2014-11-26 16:28:50 +0000
> @@ -0,0 +1,166 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +#ifndef CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
> +#define CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
> +
> +#include <core/media/apparmor/context.h>
> +#include <core/media/apparmor/dbus.h>
> +
> +#include <functional>
> +#include <memory>
> +#include <regex>
> +#include <string>
> +#include <vector>
> +
> +namespace core
> +{
> +namespace dbus
> +{
> +class Bus;
> +}
> +namespace ubuntu

Can we add a space here for readability?

> +{
> +namespace media
> +{
> +namespace helper
> +{
> +struct ExternalServices;
> +}
> +namespace apparmor
> +{
> +// Collects Ubuntu-specific apparmor conventions, e.g., format
> +// of short and full package names as well as convenience functionality
> +// to inspect apparmor::Context instances.
> +namespace ubuntu
> +{
> +// The unconfined profile, unconditionally trusted
> +// by the system.
> +static constexpr const char* unconfined
> +{
> +    "unconfined"
> +};
> +
> +class Context : public apparmor::Context
> +{
> +public:
> +    // Constructs a new Context instance for the given raw name.
> +    // Throws std::logic_error for empty names or for names not
> +    // complying to Ubuntu conventions.
> +    Context(const std::string& name);
> +
> +    // Returns true iff the context is unconfined.
> +    virtual bool is_unconfined() const;
> +
> +    // Returns true iff the context contains a package name.
> +    virtual bool has_package_name() const;
> +
> +    // Returns the package name or throws if no package name can be found.
> +    virtual std::string package_name() const;
> +
> +private:
> +    std::smatch match_;
> +    const bool unconfined_;
> +    const bool has_package_name_;
> +};
> +
> +// Abstracts query for the apparmor context of an incoming request
> +class RequestContextResolver
> +{
> +public:
> +    // To save us some typing.
> +    typedef std::shared_ptr<RequestContextResolver> Ptr;
> +
> +    // Callback for resolve context operations.
> +    typedef std::function<void(const Context&)> ResolveCallback;
> +
> +    // Resolves the given name (of a dbus participant) to its apparmor context,
> +    // invoking the callback whenever a result is available.
> +    virtual void resolve_context_for_dbus_name_async(const std::string& name, ResolveCallback cb) = 0;
> +
> +protected:
> +    RequestContextResolver() = default;
> +    RequestContextResolver(const RequestContextResolver&) = delete;
> +    virtual ~RequestContextResolver() = default;
> +    RequestContextResolver& operator=(const RequestContextResolver&) = delete;
> +};
> +
> +// An implementation of RequestContextResolver that queries the dbus
> +// daemon to resolve the apparmor context.
> +class DBusDaemonRequestContextResolver : public RequestContextResolver
> +{
> +public:
> +    // To save us some typing.
> +    typedef std::shared_ptr<DBusDaemonRequestContextResolver> Ptr;
> +
> +    // Constructs a new instance for the given bus connection.
> +    DBusDaemonRequestContextResolver(const core::dbus::Bus::Ptr &);
> +
> +    // From RequestContextResolver
> +    void resolve_context_for_dbus_name_async(const std::string& name, ResolveCallback) override;
> +
> +private:
> +    org::freedesktop::dbus::DBus::Stub dbus_daemon;
> +};
> +
> +// Abstracts an apparmor-based authentication of
> +// incoming requests from clients.
> +class RequestAuthenticator
> +{
> +public:
> +    // To save us some typing.
> +    typedef std::shared_ptr<RequestAuthenticator> Ptr;
> +
> +    // Return type of an authentication call.
> +    typedef std::tuple
> +    <
> +        bool,       // True if authenticated, false if not.
> +        std::string // Reason for the result.
> +    > Result;
> +
> +    virtual ~RequestAuthenticator() = default;
> +
> +    // Returns true iff the client identified by the given apparmor::Context is allowed
> +    // to access the given uri, false otherwise.
> +    virtual Result authenticate_open_uri_request(const Context&, const std::string& uri) = 0;
> +
> +protected:
> +    RequestAuthenticator() = default;
> +    RequestAuthenticator(const RequestAuthenticator&) = default;   
> +    RequestAuthenticator& operator=(const RequestAuthenticator&) = default;
> +};
> +
> +// Takes the existing logic and exposes it as an implementation
> +// of the RequestAuthenticator interface.
> +struct ExistingAuthenticator : public RequestAuthenticator
> +{
> +    ExistingAuthenticator() = default;
> +    // From RequestAuthenticator
> +    Result authenticate_open_uri_request(const Context&, const std::string& uri) override;
> +};
> +
> +// Returns the platform-default implementation of RequestContextResolver.
> +RequestContextResolver::Ptr make_platform_default_request_context_resolver(helper::ExternalServices& es);
> +// Returns the platform-default implementation of RequestAuthenticator.
> +RequestAuthenticator::Ptr make_platform_default_request_authenticator();
> +}
> +}
> +}
> +}
> +}
> +
> +#endif // CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
> 
> === modified file 'src/core/media/player.cpp'
> --- src/core/media/player.cpp	2014-11-26 16:28:50 +0000
> +++ src/core/media/player.cpp	2014-11-26 16:28:50 +0000
> @@ -31,7 +31,6 @@
>  {
>      static const media::Player::Configuration config
>      {
> -        std::string{""},
>          0,
>          nullptr,
>          nullptr
> 
> === modified file 'src/core/media/player_configuration.h'
> --- src/core/media/player_configuration.h	2014-09-09 10:28:32 +0000
> +++ src/core/media/player_configuration.h	2014-11-26 16:28:50 +0000
> @@ -27,9 +27,6 @@
>  // to the implementation in a way that is opaque to the client.
>  struct core::ubuntu::media::Player::Configuration
>  {
> -    // An identifier that is helpful in referencing the player instance
> -    // across multiple services.
> -    std::string identity;
>      // Unique key for identifying the session.
>      core::ubuntu::media::Player::PlayerKey key;
>      // The bus connection to expose objects on.
> 
> === modified file 'src/core/media/player_implementation.cpp'
> --- src/core/media/player_implementation.cpp	2014-11-26 16:28:50 +0000
> +++ src/core/media/player_implementation.cpp	2014-11-26 16:28:50 +0000
> @@ -260,7 +260,8 @@
>            {
>                config.bus,
>                config.session,
> -              config.identity
> +              config.request_context_resolver,
> +              config.request_authenticator
>            }
>        },
>        d{std::make_shared<Private>(this, config)}
> 
> === modified file 'src/core/media/player_implementation.h'
> --- src/core/media/player_implementation.h	2014-11-26 16:28:50 +0000
> +++ src/core/media/player_implementation.h	2014-11-26 16:28:50 +0000
> @@ -21,6 +21,7 @@
>  
>  #include "player_skeleton.h"
>  
> +#include "apparmor/ubuntu.h"
>  #include "client_death_observer.h"
>  #include "power/state_controller.h"
>  
> @@ -41,13 +42,14 @@
>      // All creation time arguments go here
>      struct Configuration
>      {
> -        std::string identity;
>          std::shared_ptr<core::dbus::Bus> bus;
>          std::shared_ptr<core::dbus::Object> session;
>          std::shared_ptr<Service> service;
>          PlayerKey key;
>  
>          // Functional dependencies
> +        apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
> +        apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
>          ClientDeathObserver::Ptr client_death_observer;
>          power::StateController::Ptr power_state_controller;
>      };
> 
> === modified file 'src/core/media/player_skeleton.cpp'
> --- src/core/media/player_skeleton.cpp	2014-11-26 16:28:50 +0000
> +++ src/core/media/player_skeleton.cpp	2014-11-26 16:28:50 +0000
> @@ -17,15 +17,16 @@
>   *              Jim Hodapp <jim.hodapp at canonical.com>
>   */
>  
> -#include "apparmor.h"
>  #include "codec.h"
>  #include "engine.h"
> +#include "external_services.h"
>  #include "player_skeleton.h"
>  #include "player_traits.h"
>  #include "property_stub.h"
>  #include "the_session_bus.h"
>  #include "xesam.h"
>  
> +#include "apparmor/ubuntu.h"
>  #include "mpris/media_player2.h"
>  #include "mpris/metadata.h"
>  #include "mpris/player.h"
> @@ -44,15 +45,15 @@
>  struct media::PlayerSkeleton::Private
>  {
>      Private(media::PlayerSkeleton* player,
> -            const std::string& identity,
>              const std::shared_ptr<core::dbus::Bus>& bus,
> -            const std::shared_ptr<core::dbus::Object>& session)
> +            const std::shared_ptr<core::dbus::Object>& session,
> +            const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
> +            const apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
>          : impl(player),
> -          identity(identity),
>            bus(bus),
>            object(session),
> -          apparmor_session(nullptr),
> -          dbus_stub{bus},
> +          request_context_resolver{request_context_resolver},
> +          request_authenticator{request_authenticator},
>            skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}},
>            signals
>            {
> @@ -162,82 +163,6 @@
>          bus->send(reply);
>      }
>  
> -    bool does_client_have_access(const std::string& context, const std::string& uri)
> -    {
> -        if (context.empty() || uri.empty())
> -        {
> -            std::cout << "Client denied access since context or uri are empty" << std::endl;
> -            return false;
> -        }
> -
> -        if (context == "unconfined")
> -        {
> -            std::cout << "Client allowed access since it's unconfined" << std::endl;
> -            return true;
> -        }
> -
> -        size_t pos = context.find_first_of('_');
> -        if (pos == std::string::npos)
> -        {
> -            std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl;
> -            return false;
> -        }
> -
> -        const std::string pkgname = context.substr(0, pos);
> -        std::cout << "client pkgname: " << pkgname << std::endl;
> -        std::cout << "uri: " << uri << std::endl;
> -
> -        // All confined apps can access their own files
> -        if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos
> -                || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos)
> -        {
> -            std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl;
> -            return true;
> -        }
> -        else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos
> -                && uri.find(pkgname) != std::string::npos)
> -        {
> -            std::cout << "Client can access content in own opt directory" << std::endl;
> -            return true;
> -        }
> -        else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos
> -                || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos)
> -                && pkgname == "com.ubuntu.camera")
> -        {
> -            std::cout << "Camera app can access ui sounds" << std::endl;
> -            return true;
> -        }
> -        // TODO: Check if the trust store previously allowed direct access to uri
> -
> -        // Check in ~/Music and ~/Videos
> -        // TODO: when the trust store lands, check it to see if this app can access the dirs and
> -        // then remove the explicit whitelist of the music-app, and gallery-app
> -        else if ((pkgname == "com.ubuntu.music" || pkgname == "com.ubuntu.gallery") &&
> -                (uri.find(std::string("Music/")) != std::string::npos
> -                || uri.find(std::string("Videos/")) != std::string::npos
> -                || uri.find(std::string("/media")) != std::string::npos))
> -        {
> -            std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl;
> -            return true;
> -        }
> -        else if (uri.find(std::string("/usr/share/sounds")) != std::string::npos)
> -        {
> -            std::cout << "Client can access content in /usr/share/sounds" << std::endl;
> -            return true;
> -        }
> -        else if (uri.find(std::string("http://")) != std::string::npos
> -                || uri.find(std::string("rtsp://")) != std::string::npos)
> -        {
> -            std::cout << "Client can access streaming content" << std::endl;
> -            return true;
> -        }
> -        else
> -        {
> -            std::cout << "Client denied access to open_uri()" << std::endl;
> -            return false;
> -        }
> -    }
> -
>      void handle_key(const core::dbus::Message::Ptr& in)
>      {
>          auto reply = dbus::Message::make_method_return(in);
> @@ -247,15 +172,15 @@
>  
>      void handle_open_uri(const core::dbus::Message::Ptr& in)
>      {
> -        dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)
> +        request_context_resolver->resolve_context_for_dbus_name_async(in->sender(), [this, in](const media::apparmor::ubuntu::Context& context)
>          {
>              Track::UriType uri;
>              in->reader() >> uri;
>  
> -            bool have_access = does_client_have_access(profile, uri);
> +            auto result = request_authenticator->authenticate_open_uri_request(context, uri);
>  
>              auto reply = dbus::Message::make_method_return(in);
> -            reply->writer() << (have_access ? impl->open_uri(uri) : false);
> +            reply->writer() << (std::get<0>(result) ? impl->open_uri(uri) : false);
>  
>              bus->send(reply);
>          });
> @@ -283,12 +208,10 @@
>      }
>  
>      media::PlayerSkeleton* impl;
> -    std::string identity;
>      dbus::Bus::Ptr bus;
>      dbus::Object::Ptr object;
> -    dbus::Object::Ptr apparmor_session;
> -
> -    org::freedesktop::dbus::DBus::Stub dbus_stub;
> +    media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
> +    media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
>  
>      mpris::Player::Skeleton skeleton;
>  
> @@ -334,7 +257,7 @@
>  };
>  
>  media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)
> -        : d(new Private{this, config.identity, config.bus, config.session})
> +        : d(new Private{this, config.bus, config.session, config.request_context_resolver, config.request_authenticator})
>  {
>      // Setup method handlers for mpris::Player methods.
>      auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);
> 
> === modified file 'src/core/media/player_skeleton.h'
> --- src/core/media/player_skeleton.h	2014-11-26 16:28:50 +0000
> +++ src/core/media/player_skeleton.h	2014-11-26 16:28:50 +0000
> @@ -24,6 +24,7 @@
>  
>  #include "player_traits.h"
>  
> +#include "apparmor/ubuntu.h"
>  #include "mpris/player.h"
>  
>  #include <core/dbus/skeleton.h>
> @@ -37,6 +38,11 @@
>  {
>  namespace media
>  {
> +namespace helper
> +{
> +struct ExternalServices;
> +}
> +
>  class Service;
>  
>  class PlayerSkeleton : public core::ubuntu::media::Player
> @@ -83,9 +89,9 @@
>          std::shared_ptr<core::dbus::Bus> bus;
>          // The session object that we want to expose the skeleton upon.
>          std::shared_ptr<core::dbus::Object> session;
> -        // Our identity, an identifier we pass out to other parts of the system.
> -        // Defaults to the short app id (${PKG_NAME}_${APP}).
> -        std::string identity;
> +        // Our functional dependencies.
> +        apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
> +        apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
>      };
>  
>      PlayerSkeleton(const Configuration& configuration);
> 
> === modified file 'src/core/media/service_implementation.cpp'
> --- src/core/media/service_implementation.cpp	2014-11-26 16:28:50 +0000
> +++ src/core/media/service_implementation.cpp	2014-11-26 16:28:50 +0000
> @@ -22,6 +22,7 @@
>  
>  #include "service_implementation.h"
>  
> +#include "apparmor/ubuntu.h"
>  #include "audio/output_observer.h"
>  #include "client_death_observer.h"
>  #include "player_configuration.h"
> @@ -59,6 +60,8 @@
>            client_death_observer(media::platform_default_client_death_observer()),
>            recorder_observer(media::make_platform_default_recorder_observer()),
>            audio_output_observer(media::audio::make_platform_default_output_observer()),
> +          request_context_resolver(media::apparmor::ubuntu::make_platform_default_request_context_resolver(configuration.external_services)),
> +          request_authenticator(media::apparmor::ubuntu::make_platform_default_request_authenticator()),
>            call_monitor(media::telephony::make_platform_default_call_monitor())
>      {
>      }
> @@ -81,12 +84,14 @@
>      media::ClientDeathObserver::Ptr client_death_observer;
>      media::RecorderObserver::Ptr recorder_observer;
>      media::audio::OutputObserver::Ptr audio_output_observer;
> -
> +    media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
> +    media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
>      media::telephony::CallMonitor::Ptr call_monitor;
>      std::list<media::Player::PlayerKey> paused_sessions;
>  };
>  
> -media::ServiceImplementation::ServiceImplementation(const Configuration& configuration) : d(new Private(configuration))
> +media::ServiceImplementation::ServiceImplementation(const Configuration& configuration)
> +    : d(new Private(configuration))
>  {
>      d->battery_observer->level().changed().connect([this](const media::power::Level& level)
>      {
> @@ -145,11 +150,12 @@
>  {
>      auto player = std::make_shared<media::PlayerImplementation>(media::PlayerImplementation::Configuration
>      {
> -        conf.identity,
>          conf.bus,
>          conf.session,
>          shared_from_this(),
>          conf.key,
> +        d->request_context_resolver,
> +        d->request_authenticator,
>          d->client_death_observer,
>          d->power_state_controller
>      });
> 
> === modified file 'src/core/media/service_skeleton.cpp'
> --- src/core/media/service_skeleton.cpp	2014-11-07 15:49:20 +0000
> +++ src/core/media/service_skeleton.cpp	2014-11-26 16:28:50 +0000
> @@ -19,8 +19,6 @@
>  
>  #include "service_skeleton.h"
>  
> -#include "apparmor.h"
> -
>  #include "mpris/media_player2.h"
>  #include "mpris/metadata.h"
>  #include "mpris/player.h"
> @@ -55,7 +53,6 @@
>          : impl(impl),
>            object(impl->access_service()->add_object_for_path(
>                       dbus::traits::Service<media::Service>::object_path())),
> -          dbus_stub(impl->access_bus()),
>            exported(impl->access_bus(), resolver)
>      {
>          object->install_method_handler<mpris::Service::CreateSession>(
> @@ -80,40 +77,36 @@
>          dbus::types::ObjectPath op{ss.str()};
>          media::Player::PlayerKey key{session_counter};
>  
> -        dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg, op, key](const std::string& profile)
> -        {
> -            media::Player::Configuration config
> -            {
> -                profile,
> -                key,
> -                impl->access_bus(),
> -                impl->access_service()->add_object_for_path(op)
> -            };
> -
> -            try
> -            {
> -                auto session = impl->create_session(config);
> -
> -                bool inserted = false;
> -                std::tie(std::ignore, inserted)
> -                        = session_store.insert(std::make_pair(key, session));
> -
> -                if (!inserted)
> -                    throw std::runtime_error("Problem persisting session in session store.");
> -
> -                auto reply = dbus::Message::make_method_return(msg);
> -                reply->writer() << op;
> -
> -                impl->access_bus()->send(reply);
> -            } catch(const std::runtime_error& e)
> -            {
> -                auto reply = dbus::Message::make_error(
> -                            msg,
> -                            mpris::Service::Errors::CreatingSession::name(),
> -                            e.what());
> -                impl->access_bus()->send(reply);
> -            }
> -        });
> +        media::Player::Configuration config
> +        {
> +            key,
> +            impl->access_bus(),
> +            impl->access_service()->add_object_for_path(op)
> +        };
> +
> +        try
> +        {
> +            auto session = impl->create_session(config);
> +
> +            bool inserted = false;
> +            std::tie(std::ignore, inserted)
> +                    = session_store.insert(std::make_pair(key, session));
> +
> +            if (!inserted)
> +                throw std::runtime_error("Problem persisting session in session store.");
> +
> +            auto reply = dbus::Message::make_method_return(msg);
> +            reply->writer() << op;
> +
> +            impl->access_bus()->send(reply);
> +        } catch(const std::runtime_error& e)
> +        {
> +            auto reply = dbus::Message::make_error(
> +                        msg,
> +                        mpris::Service::Errors::CreatingSession::name(),
> +                        e.what());
> +            impl->access_bus()->send(reply);
> +        }
>      }
>  
>      void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
> @@ -130,8 +123,6 @@
>      media::ServiceSkeleton* impl;
>      dbus::Object::Ptr object;
>  
> -    // We query the apparmor profile to obtain an identity for players.
> -    org::freedesktop::dbus::DBus::Stub dbus_stub;
>      // We track all running player instances.
>      std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> session_store;
>      // We expose the entire service as an MPRIS player.
> 


-- 
https://code.launchpad.net/~thomas-voss/media-hub/introduce-apparmor-interfaces/+merge/242943
Your team Ubuntu Phablet Team is subscribed to branch lp:media-hub.



More information about the Ubuntu-reviews mailing list