Mir

Merge lp:~alan-griffiths/mir/fix-1314574 into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1644
Proposed branch: lp:~alan-griffiths/mir/fix-1314574
Merge into: lp:mir
Prerequisite: lp:~alan-griffiths/mir/basic-test-fixture
Diff against target: 242 lines (+94/-24)
6 files modified
include/server/mir/frontend/session_credentials.h (+0/-3)
src/server/frontend/published_socket_connector.cpp (+6/-0)
src/server/frontend/session_credentials.cpp (+0/-18)
src/server/frontend/socket_messenger.cpp (+54/-3)
src/server/frontend/socket_messenger.h (+6/-0)
tests/acceptance-tests/test_trust_session_helper.cpp (+28/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/fix-1314574
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Kevin DuBois (community) Approve
Alexandros Frantzis (community) Approve
Review via email: mp+219814@code.launchpad.net

Commit message

frontend: get the credentials of the real client process when it connects

Description of the change

frontend: get the credentials of the real client process when it connects

Posix mandates that with previously used SO_PEERCRED "The returned credentials are those that were in effect at the time of the call to connect(2) or socketpair(2)."

Note that privileged processes *can* spoof their SCM_CREDENTIALS as I don't think it matters for our use cases. But this seems to be the best we can achieve without relying on non-portable techniques (e.g. apparmor security contexts).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Fetched 19.1 MB in 33s (573 kB/s)
W: Failed to fetch bzip2:/var/lib/apt/lists/partial/ports.ubuntu.com_ubuntu-ports_dists_utopic_universe_source_Sources Hash Sum mismatch

W: Failed to fetch bzip2:/var/lib/apt/lists/partial/ports.ubuntu.com_ubuntu-ports_dists_utopic_universe_binary-armhf_Packages Hash Sum mismatch

Triggering rebuild

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

153 + auto const ucredp = (struct ucred *) CMSG_DATA(CMSG_FIRSTHDR(&msgh));

C cast.

210 +// Disabled as we can't be sure the mir_demo_client_basic is about

Why depend on mir_demo_client_basic instead of creating a new process and running mir_connect() ourselves? We can also record the actual client PID this way, and compare it directly with what the server got.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> 153 + auto const ucredp = (struct ucred *)
> CMSG_DATA(CMSG_FIRSTHDR(&msgh));
>
> C cast.

Done

> 210 +// Disabled as we can't be sure the mir_demo_client_basic is about
>
> Why depend on mir_demo_client_basic instead of creating a new process and
> running mir_connect() ourselves? We can also record the actual client PID this
> way, and compare it directly with what the server got.

I did consider this - but wasn't sure I wanted to get involved with forking a process that already has a mir server running in it (and a socket pair). (Or communicating with one forked earlier after obtaining the socket.) I've been thinking that a small scriptable client might be good for several tests. But including that work in this MP seemed disproportionate.

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> I did consider this - but wasn't sure I wanted to get involved with forking a process that already
> has a mir server running in it (and a socket pair). (Or communicating with one forked earlier after
> obtaining the socket.) I've been thinking that a small scriptable client might be good for several
> tests. But including that work in this MP seemed disproportionate.

The scriptable client would be a solution for some cases, but I prefer the flexebility of running completely custom code for clients if possible (especially if some synchronization is needed).

I think that an "exec_in_clean_process(std::function<>, std::vector<int> excluded_fds)" that does a fork followed by an FD cleaning pass (close all open fds, except the supplied list that contains fd numbers for the server socket and pipes used for synchronization (mtf::CrossProcessSync) etc) would be enough.

Anyway, I am happy as long as we have a plan for this for the short(-ish)-term

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Anyway, I am happy as long as we have a plan for this for the short(-ish)-term

We have some convergent thoughts, not a plan. Marking with a "TODO" comment

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

okay by me

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'include/server/mir/frontend/session_credentials.h'
--- include/server/mir/frontend/session_credentials.h 2014-05-19 02:55:29 +0000
+++ include/server/mir/frontend/session_credentials.h 2014-05-20 14:06:48 +0000
@@ -27,8 +27,6 @@
27class SessionCredentials27class SessionCredentials
28{28{
29public:29public:
30 static SessionCredentials from_socket_fd(int socket_fd);
31
32 SessionCredentials(pid_t pid, uid_t uid, gid_t gid);30 SessionCredentials(pid_t pid, uid_t uid, gid_t gid);
3331
34 pid_t pid() const;32 pid_t pid() const;
@@ -41,7 +39,6 @@
41 pid_t the_pid;39 pid_t the_pid;
42 uid_t the_uid;40 uid_t the_uid;
43 gid_t the_gid;41 gid_t the_gid;
44
45};42};
46}43}
47}44}
4845
=== modified file 'src/server/frontend/published_socket_connector.cpp'
--- src/server/frontend/published_socket_connector.cpp 2014-05-19 02:55:29 +0000
+++ src/server/frontend/published_socket_connector.cpp 2014-05-20 14:06:48 +0000
@@ -205,6 +205,12 @@
205 std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const205 std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const
206{206{
207 report->creating_session_for(server_socket->native_handle());207 report->creating_session_for(server_socket->native_handle());
208
209 /* We set the SO_PASSCRED socket option in order to receive credentials */
210 auto const optval = 1;
211 if (setsockopt(server_socket->native_handle(), SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1)
212 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set SO_PASSCRED"));
213
208 connection_creator->create_connection_for(server_socket, {connect_handler, this});214 connection_creator->create_connection_for(server_socket, {connect_handler, this});
209}215}
210216
211217
=== modified file 'src/server/frontend/session_credentials.cpp'
--- src/server/frontend/session_credentials.cpp 2014-05-19 02:55:29 +0000
+++ src/server/frontend/session_credentials.cpp 2014-05-20 14:06:48 +0000
@@ -17,26 +17,8 @@
1717
18#include "mir/frontend/session_credentials.h"18#include "mir/frontend/session_credentials.h"
1919
20#include <sys/socket.h>
21
22#include <stdexcept>
23#include <boost/throw_exception.hpp>
24
25namespace mf = mir::frontend;20namespace mf = mir::frontend;
2621
27mf::SessionCredentials mf::SessionCredentials::from_socket_fd(int socket_fd)
28{
29 struct ucred cr;
30 socklen_t cl = sizeof(cr);
31
32 auto status = getsockopt(socket_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl);
33
34 if (status)
35 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to query client socket credentials"));
36
37 return {cr.pid, cr.uid, cr.gid};
38}
39
40mf::SessionCredentials::SessionCredentials(pid_t pid, uid_t uid, gid_t gid) :22mf::SessionCredentials::SessionCredentials(pid_t pid, uid_t uid, gid_t gid) :
41 the_pid{pid},23 the_pid{pid},
42 the_uid{uid},24 the_uid{uid},
4325
=== modified file 'src/server/frontend/socket_messenger.cpp'
--- src/server/frontend/socket_messenger.cpp 2014-05-20 08:38:05 +0000
+++ src/server/frontend/socket_messenger.cpp 2014-05-20 14:06:48 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2013 Canonical Ltd.2 * Copyright © 2013-2014 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as5 * it under the terms of the GNU General Public License version 3 as
@@ -21,6 +21,8 @@
21#include "mir/frontend/session_credentials.h"21#include "mir/frontend/session_credentials.h"
22#include "mir/variable_length_array.h"22#include "mir/variable_length_array.h"
2323
24#include <boost/throw_exception.hpp>
25
24#include <errno.h>26#include <errno.h>
25#include <string.h>27#include <string.h>
2628
@@ -36,9 +38,27 @@
36{38{
37}39}
3840
41mf::SessionCredentials mfd::SocketMessenger::creator_creds() const
42{
43 struct ucred cr;
44 socklen_t cl = sizeof(cr);
45
46 auto status = getsockopt(socket->native_handle(), SOL_SOCKET, SO_PEERCRED, &cr, &cl);
47
48 if (status)
49 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to query client socket credentials"));
50
51 return {cr.pid, cr.uid, cr.gid};
52}
53
39mf::SessionCredentials mfd::SocketMessenger::client_creds()54mf::SessionCredentials mfd::SocketMessenger::client_creds()
40{55{
41 return mf::SessionCredentials::from_socket_fd(socket->native_handle());56 if (session_creds.pid() != 0)
57 return session_creds;
58
59 // We've not got the credentials from client yet.
60 // Return the credentials that created the socket instead.
61 return creator_creds();
42}62}
4363
44void mfd::SocketMessenger::send(char const* data, size_t length, FdSets const& fd_set)64void mfd::SocketMessenger::send(char const* data, size_t length, FdSets const& fd_set)
@@ -92,7 +112,7 @@
92 message->cmsg_len = header.msg_controllen;112 message->cmsg_len = header.msg_controllen;
93 message->cmsg_level = SOL_SOCKET;113 message->cmsg_level = SOL_SOCKET;
94 message->cmsg_type = SCM_RIGHTS;114 message->cmsg_type = SCM_RIGHTS;
95 int *data = (int *)CMSG_DATA(message);115 int *data = reinterpret_cast<int*>(CMSG_DATA(message));
96 int i = 0;116 int i = 0;
97 for (auto &fd: fds)117 for (auto &fd: fds)
98 data[i++] = fd;118 data[i++] = fd;
@@ -128,7 +148,38 @@
128148
129size_t mfd::SocketMessenger::available_bytes()149size_t mfd::SocketMessenger::available_bytes()
130{150{
151 // We call available_bytes() once the client is talking to us
152 // so this is a pragmatic place to grab the session credentials
153 if (session_creds.pid() == 0)
154 update_session_creds();
155
131 boost::asio::socket_base::bytes_readable command{true};156 boost::asio::socket_base::bytes_readable command{true};
132 socket->io_control(command);157 socket->io_control(command);
133 return command.get();158 return command.get();
134}159}
160
161void mfd::SocketMessenger::update_session_creds()
162{
163 union {
164 struct cmsghdr cmh;
165 char control[CMSG_SPACE(sizeof(ucred))];
166 } control_un;
167
168 control_un.cmh.cmsg_len = CMSG_LEN(sizeof(ucred));
169 control_un.cmh.cmsg_level = SOL_SOCKET;
170 control_un.cmh.cmsg_type = SCM_CREDENTIALS;
171
172 msghdr msgh;
173 msgh.msg_name = nullptr;
174 msgh.msg_namelen = 0;
175 msgh.msg_iov = nullptr;
176 msgh.msg_iovlen = 0;
177 msgh.msg_control = control_un.control;
178 msgh.msg_controllen = sizeof(control_un.control);
179
180 if (recvmsg(socket->native_handle(), &msgh, MSG_PEEK) != -1)
181 {
182 auto const ucredp = reinterpret_cast<ucred*>(CMSG_DATA(CMSG_FIRSTHDR(&msgh)));
183 session_creds = {ucredp->pid, ucredp->uid, ucredp->gid};
184 }
185}
135186
=== modified file 'src/server/frontend/socket_messenger.h'
--- src/server/frontend/socket_messenger.h 2014-05-16 16:46:13 +0000
+++ src/server/frontend/socket_messenger.h 2014-05-20 14:06:48 +0000
@@ -20,6 +20,7 @@
20#define MIR_FRONTEND_SOCKET_MESSENGER_H_20#define MIR_FRONTEND_SOCKET_MESSENGER_H_
21#include "message_sender.h"21#include "message_sender.h"
22#include "message_receiver.h"22#include "message_receiver.h"
23#include "mir/frontend/session_credentials.h"
23#include <mutex>24#include <mutex>
2425
25namespace mir26namespace mir
@@ -42,9 +43,14 @@
42 SessionCredentials client_creds() override;43 SessionCredentials client_creds() override;
4344
44private:45private:
46 void update_session_creds();
47 SessionCredentials creator_creds() const;
48
45 std::shared_ptr<boost::asio::local::stream_protocol::socket> socket;49 std::shared_ptr<boost::asio::local::stream_protocol::socket> socket;
4650
47 std::mutex message_lock;51 std::mutex message_lock;
52 SessionCredentials session_creds{0, 0, 0};
53
48 void send_fds_locked(std::unique_lock<std::mutex> const& lock, std::vector<int32_t> const& fds);54 void send_fds_locked(std::unique_lock<std::mutex> const& lock, std::vector<int32_t> const& fds);
49};55};
50}56}
5157
=== modified file 'tests/acceptance-tests/test_trust_session_helper.cpp'
--- tests/acceptance-tests/test_trust_session_helper.cpp 2014-05-19 02:55:29 +0000
+++ tests/acceptance-tests/test_trust_session_helper.cpp 2014-05-20 14:06:48 +0000
@@ -27,6 +27,7 @@
27#include "mir_test_framework/stubbed_server_configuration.h"27#include "mir_test_framework/stubbed_server_configuration.h"
28#include "mir_test_framework/basic_client_server_fixture.h"28#include "mir_test_framework/basic_client_server_fixture.h"
29#include "mir_test_doubles/fake_ipc_factory.h"29#include "mir_test_doubles/fake_ipc_factory.h"
30#include "mir_test/popen.h"
3031
31#include <gtest/gtest.h>32#include <gtest/gtest.h>
32#include <gmock/gmock.h>33#include <gmock/gmock.h>
@@ -149,6 +150,8 @@
149 sprintf(client_connect_string, "fd://%d", fd);150 sprintf(client_connect_string, "fd://%d", fd);
150 return client_connect_string;151 return client_connect_string;
151 }152 }
153
154 MOCK_METHOD1(process_line, void(std::string const&));
152};155};
153156
154void client_fd_callback(MirConnection*, size_t count, int const* fds, void* context)157void client_fd_callback(MirConnection*, size_t count, int const* fds, void* context)
@@ -185,3 +188,28 @@
185188
186 mir_connection_release(client_connection);189 mir_connection_release(client_connection);
187}190}
191
192// TODO we need a nice way to run this (and similar tests that require a separate client process) in CI
193// Disabled as we can't be sure the mir_demo_client_basic is about
194TEST_F(TrustSessionHelper, DISABLED_client_pid_is_associated_with_session)
195{
196 auto const server_pid = getpid();
197
198 mir_connection_new_fds_for_trusted_clients(connection, 1, &client_fd_callback, this);
199 wait_for_callback(std::chrono::milliseconds(500));
200
201 InSequence seq;
202 EXPECT_CALL(*this, process_line(StrEq("Starting")));
203 EXPECT_CALL(*this, process_line(StrEq("Connected")));
204 EXPECT_CALL(*this, process_line(StrEq("Surface created")));
205 EXPECT_CALL(*this, process_line(StrEq("Surface released")));
206 EXPECT_CALL(*this, process_line(StrEq("Connection released")));
207
208 mir::test::Popen output(std::string("bin/mir_demo_client_basic -m ") + fd_connect_string(actual_fds[0]));
209
210 std::string line;
211 while (output.get_line(line)) process_line(line);
212
213 EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(0));
214 EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(server_pid));
215}

Subscribers

People subscribed via source and target branches