~jamesh/thumbnailer/use-aa-query-label

« back to all changes in this revision

Viewing changes to src/service/credentialscache.cpp

  • Committer: Tarmac
  • Author(s): James Henstridge
  • Date: 2015-06-15 01:56:18 UTC
  • mfrom: (213.1.10 use-aa-query-label)
  • Revision ID: tarmac-20150615015618-n47qom31w779seld
Track the credentials (user ID, AppArmor label) of clients connecting to the D-Bus service.  This is not yet used to make security decisions.

Approved by Michi Henning, PS Jenkins bot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2015 Canonical, Ltd.
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or modify it under
 
5
 * the terms of version 3 of the GNU General Public License as published
 
6
 * by the Free Software Foundation.
 
7
 *
 
8
 * This library is distributed in the hope that it will be useful, but WITHOUT
 
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
10
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 
11
 * details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authors:
 
17
 *    James Henstridge <james.henstridge@canonical.com>
 
18
 */
 
19
 
 
20
#include "credentialscache.h"
 
21
 
 
22
#include <QDBusPendingCallWatcher>
 
23
 
 
24
#include <assert.h>
 
25
#include <vector>
 
26
#include <sys/apparmor.h>
 
27
 
 
28
using namespace std;
 
29
 
 
30
namespace {
 
31
 
 
32
char const DBUS_BUS_NAME[] = "org.freedesktop.DBus";
 
33
char const DBUS_BUS_PATH[] = "/org/freedesktop/DBus";
 
34
 
 
35
char const UNIX_USER_ID[] = "UnixUserID";
 
36
char const LINUX_SECURITY_LABEL[] = "LinuxSecurityLabel";
 
37
 
 
38
int const MAX_CACHE_SIZE = 50;
 
39
 
 
40
}
 
41
 
 
42
namespace unity
 
43
{
 
44
 
 
45
namespace thumbnailer
 
46
{
 
47
 
 
48
namespace service
 
49
{
 
50
 
 
51
struct CredentialsCache::Request
 
52
{
 
53
    QDBusPendingCallWatcher watcher;
 
54
    std::vector<CredentialsCache::Callback> callbacks;
 
55
 
 
56
    Request(QDBusPendingReply<QVariantMap> call) : watcher(call) {}
 
57
};
 
58
 
 
59
CredentialsCache::CredentialsCache(QDBusConnection const& bus)
 
60
    : bus_daemon_(DBUS_BUS_NAME, DBUS_BUS_PATH, bus)
 
61
    , apparmor_enabled_(aa_is_enabled())
 
62
{
 
63
}
 
64
 
 
65
CredentialsCache::~CredentialsCache() = default;
 
66
 
 
67
void CredentialsCache::get(QString const& peer, Callback callback)
 
68
{
 
69
    // Return the credentials directly if they are cached
 
70
    try
 
71
    {
 
72
        Credentials const& credentials = cache_.at(peer);
 
73
        callback(credentials);
 
74
        return;
 
75
    }
 
76
    catch (std::out_of_range const &)
 
77
    {
 
78
        // ignore
 
79
    }
 
80
 
 
81
    // If the credentials exist in the previous generation of the
 
82
    // cache, move them to the current generation.
 
83
    try
 
84
    {
 
85
        Credentials& credentials = old_cache_.at(peer);
 
86
        cache_.emplace(peer, std::move(credentials));
 
87
        old_cache_.erase(peer);
 
88
        callback(cache_.at(peer));
 
89
        return;
 
90
    }
 
91
    catch (std::out_of_range const &)
 
92
    {
 
93
        // ignore
 
94
    }
 
95
 
 
96
    // If the credentials are already being requested, add ourselves
 
97
    // to the callback list.
 
98
    try
 
99
    {
 
100
        unique_ptr<Request>& request = pending_.at(peer);
 
101
        request->callbacks.push_back(callback);
 
102
        return;
 
103
    }
 
104
    catch (std::out_of_range const &)
 
105
    {
 
106
        // ignore
 
107
    }
 
108
 
 
109
    // Ask the bus daemon for the peer's credentials
 
110
    unique_ptr<Request> request(
 
111
        new Request(bus_daemon_.GetConnectionCredentials(peer)));
 
112
    QObject::connect(&request->watcher, &QDBusPendingCallWatcher::finished,
 
113
                     [this, peer](QDBusPendingCallWatcher *watcher)
 
114
                     {
 
115
                         this->received_credentials(peer, *watcher);
 
116
                     });
 
117
    request->callbacks.push_back(callback);
 
118
    pending_.emplace(peer, std::move(request));
 
119
}
 
120
 
 
121
void CredentialsCache::received_credentials(QString const& peer, QDBusPendingReply<QVariantMap> reply)
 
122
{
 
123
    Credentials credentials;
 
124
    if (reply.isError())
 
125
    {
 
126
        qWarning() << "CredentialsCache::received_credentials(): "
 
127
            "error retrieving credentials for" << peer <<
 
128
            ":" << reply.error().message();
 
129
    }
 
130
    else
 
131
    {
 
132
        credentials.valid = true;
 
133
        // The contents of this map are described in the specification here:
 
134
        // http://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-get-connection-credentials
 
135
        credentials.user = reply.value().value(UNIX_USER_ID).value<uint32_t>();
 
136
        if (apparmor_enabled_)
 
137
        {
 
138
            QByteArray label = reply.value().value(LINUX_SECURITY_LABEL).value<QByteArray>();
 
139
            if (label.size() > 0) {
 
140
                // The label is null terminated.
 
141
                assert(label[label.size()-1] == '\0');
 
142
                label.truncate(label.size() - 1);
 
143
                // Trim the mode off the end of the label.
 
144
                int pos = label.lastIndexOf(' ');
 
145
                if (pos > 0 && label.endsWith(')') && label[pos+1] == '(')
 
146
                {
 
147
                    label.truncate(pos);
 
148
                }
 
149
                credentials.label = string(label.constData(), label.size());
 
150
            }
 
151
        }
 
152
        else
 
153
        {
 
154
            // If AppArmor is not enabled, treat peer as unconfined.
 
155
            credentials.label = "unconfined";
 
156
        }
 
157
    }
 
158
 
 
159
    // If we've hit our maximum cache size, start a new generation.
 
160
    if (cache_.size() >= MAX_CACHE_SIZE)
 
161
    {
 
162
        old_cache_ = std::move(cache_);
 
163
        cache_.clear();
 
164
    }
 
165
    cache_.emplace(peer, credentials);
 
166
 
 
167
    // Notify anyone waiting on the request and remove it from the map:
 
168
    for (auto& callback : pending_.at(peer)->callbacks)
 
169
    {
 
170
        callback(credentials);
 
171
    }
 
172
    pending_.erase(peer);
 
173
}
 
174
 
 
175
}  // namespace service
 
176
 
 
177
}  // namespace thumbnailer
 
178
 
 
179
}  // namespace unity