Merge lp:~alan-griffiths/mir/fix-1314574 into lp:mir
- fix-1314574
- Merge into development-branch
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 |
Related bugs: |
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).
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1638
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
Fetched 19.1 MB in 33s (573 kB/s)
W: Failed to fetch bzip2:/
W: Failed to fetch bzip2:/
Triggering rebuild
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1638
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1638
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
153 + auto const ucredp = (struct ucred *) CMSG_DATA(
C cast.
210 +// Disabled as we can't be sure the mir_demo_
Why depend on mir_demo_
Alan Griffiths (alan-griffiths) wrote : | # |
> 153 + auto const ucredp = (struct ucred *)
> CMSG_DATA(
>
> C cast.
Done
> 210 +// Disabled as we can't be sure the mir_demo_
>
> Why depend on mir_demo_
> 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.
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_
Anyway, I am happy as long as we have a plan for this for the short(-ish)-term
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
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1639
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1641
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'include/server/mir/frontend/session_credentials.h' |
2 | --- include/server/mir/frontend/session_credentials.h 2014-05-19 02:55:29 +0000 |
3 | +++ include/server/mir/frontend/session_credentials.h 2014-05-20 14:06:48 +0000 |
4 | @@ -27,8 +27,6 @@ |
5 | class SessionCredentials |
6 | { |
7 | public: |
8 | - static SessionCredentials from_socket_fd(int socket_fd); |
9 | - |
10 | SessionCredentials(pid_t pid, uid_t uid, gid_t gid); |
11 | |
12 | pid_t pid() const; |
13 | @@ -41,7 +39,6 @@ |
14 | pid_t the_pid; |
15 | uid_t the_uid; |
16 | gid_t the_gid; |
17 | - |
18 | }; |
19 | } |
20 | } |
21 | |
22 | === modified file 'src/server/frontend/published_socket_connector.cpp' |
23 | --- src/server/frontend/published_socket_connector.cpp 2014-05-19 02:55:29 +0000 |
24 | +++ src/server/frontend/published_socket_connector.cpp 2014-05-20 14:06:48 +0000 |
25 | @@ -205,6 +205,12 @@ |
26 | std::function<void(std::shared_ptr<Session> const& session)> const& connect_handler) const |
27 | { |
28 | report->creating_session_for(server_socket->native_handle()); |
29 | + |
30 | + /* We set the SO_PASSCRED socket option in order to receive credentials */ |
31 | + auto const optval = 1; |
32 | + if (setsockopt(server_socket->native_handle(), SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) |
33 | + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to set SO_PASSCRED")); |
34 | + |
35 | connection_creator->create_connection_for(server_socket, {connect_handler, this}); |
36 | } |
37 | |
38 | |
39 | === modified file 'src/server/frontend/session_credentials.cpp' |
40 | --- src/server/frontend/session_credentials.cpp 2014-05-19 02:55:29 +0000 |
41 | +++ src/server/frontend/session_credentials.cpp 2014-05-20 14:06:48 +0000 |
42 | @@ -17,26 +17,8 @@ |
43 | |
44 | #include "mir/frontend/session_credentials.h" |
45 | |
46 | -#include <sys/socket.h> |
47 | - |
48 | -#include <stdexcept> |
49 | -#include <boost/throw_exception.hpp> |
50 | - |
51 | namespace mf = mir::frontend; |
52 | |
53 | -mf::SessionCredentials mf::SessionCredentials::from_socket_fd(int socket_fd) |
54 | -{ |
55 | - struct ucred cr; |
56 | - socklen_t cl = sizeof(cr); |
57 | - |
58 | - auto status = getsockopt(socket_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl); |
59 | - |
60 | - if (status) |
61 | - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to query client socket credentials")); |
62 | - |
63 | - return {cr.pid, cr.uid, cr.gid}; |
64 | -} |
65 | - |
66 | mf::SessionCredentials::SessionCredentials(pid_t pid, uid_t uid, gid_t gid) : |
67 | the_pid{pid}, |
68 | the_uid{uid}, |
69 | |
70 | === modified file 'src/server/frontend/socket_messenger.cpp' |
71 | --- src/server/frontend/socket_messenger.cpp 2014-05-20 08:38:05 +0000 |
72 | +++ src/server/frontend/socket_messenger.cpp 2014-05-20 14:06:48 +0000 |
73 | @@ -1,5 +1,5 @@ |
74 | /* |
75 | - * Copyright © 2013 Canonical Ltd. |
76 | + * Copyright © 2013-2014 Canonical Ltd. |
77 | * |
78 | * This program is free software: you can redistribute it and/or modify |
79 | * it under the terms of the GNU General Public License version 3 as |
80 | @@ -21,6 +21,8 @@ |
81 | #include "mir/frontend/session_credentials.h" |
82 | #include "mir/variable_length_array.h" |
83 | |
84 | +#include <boost/throw_exception.hpp> |
85 | + |
86 | #include <errno.h> |
87 | #include <string.h> |
88 | |
89 | @@ -36,9 +38,27 @@ |
90 | { |
91 | } |
92 | |
93 | +mf::SessionCredentials mfd::SocketMessenger::creator_creds() const |
94 | +{ |
95 | + struct ucred cr; |
96 | + socklen_t cl = sizeof(cr); |
97 | + |
98 | + auto status = getsockopt(socket->native_handle(), SOL_SOCKET, SO_PEERCRED, &cr, &cl); |
99 | + |
100 | + if (status) |
101 | + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to query client socket credentials")); |
102 | + |
103 | + return {cr.pid, cr.uid, cr.gid}; |
104 | +} |
105 | + |
106 | mf::SessionCredentials mfd::SocketMessenger::client_creds() |
107 | { |
108 | - return mf::SessionCredentials::from_socket_fd(socket->native_handle()); |
109 | + if (session_creds.pid() != 0) |
110 | + return session_creds; |
111 | + |
112 | + // We've not got the credentials from client yet. |
113 | + // Return the credentials that created the socket instead. |
114 | + return creator_creds(); |
115 | } |
116 | |
117 | void mfd::SocketMessenger::send(char const* data, size_t length, FdSets const& fd_set) |
118 | @@ -92,7 +112,7 @@ |
119 | message->cmsg_len = header.msg_controllen; |
120 | message->cmsg_level = SOL_SOCKET; |
121 | message->cmsg_type = SCM_RIGHTS; |
122 | - int *data = (int *)CMSG_DATA(message); |
123 | + int *data = reinterpret_cast<int*>(CMSG_DATA(message)); |
124 | int i = 0; |
125 | for (auto &fd: fds) |
126 | data[i++] = fd; |
127 | @@ -128,7 +148,38 @@ |
128 | |
129 | size_t mfd::SocketMessenger::available_bytes() |
130 | { |
131 | + // We call available_bytes() once the client is talking to us |
132 | + // so this is a pragmatic place to grab the session credentials |
133 | + if (session_creds.pid() == 0) |
134 | + update_session_creds(); |
135 | + |
136 | boost::asio::socket_base::bytes_readable command{true}; |
137 | socket->io_control(command); |
138 | return command.get(); |
139 | } |
140 | + |
141 | +void mfd::SocketMessenger::update_session_creds() |
142 | +{ |
143 | + union { |
144 | + struct cmsghdr cmh; |
145 | + char control[CMSG_SPACE(sizeof(ucred))]; |
146 | + } control_un; |
147 | + |
148 | + control_un.cmh.cmsg_len = CMSG_LEN(sizeof(ucred)); |
149 | + control_un.cmh.cmsg_level = SOL_SOCKET; |
150 | + control_un.cmh.cmsg_type = SCM_CREDENTIALS; |
151 | + |
152 | + msghdr msgh; |
153 | + msgh.msg_name = nullptr; |
154 | + msgh.msg_namelen = 0; |
155 | + msgh.msg_iov = nullptr; |
156 | + msgh.msg_iovlen = 0; |
157 | + msgh.msg_control = control_un.control; |
158 | + msgh.msg_controllen = sizeof(control_un.control); |
159 | + |
160 | + if (recvmsg(socket->native_handle(), &msgh, MSG_PEEK) != -1) |
161 | + { |
162 | + auto const ucredp = reinterpret_cast<ucred*>(CMSG_DATA(CMSG_FIRSTHDR(&msgh))); |
163 | + session_creds = {ucredp->pid, ucredp->uid, ucredp->gid}; |
164 | + } |
165 | +} |
166 | |
167 | === modified file 'src/server/frontend/socket_messenger.h' |
168 | --- src/server/frontend/socket_messenger.h 2014-05-16 16:46:13 +0000 |
169 | +++ src/server/frontend/socket_messenger.h 2014-05-20 14:06:48 +0000 |
170 | @@ -20,6 +20,7 @@ |
171 | #define MIR_FRONTEND_SOCKET_MESSENGER_H_ |
172 | #include "message_sender.h" |
173 | #include "message_receiver.h" |
174 | +#include "mir/frontend/session_credentials.h" |
175 | #include <mutex> |
176 | |
177 | namespace mir |
178 | @@ -42,9 +43,14 @@ |
179 | SessionCredentials client_creds() override; |
180 | |
181 | private: |
182 | + void update_session_creds(); |
183 | + SessionCredentials creator_creds() const; |
184 | + |
185 | std::shared_ptr<boost::asio::local::stream_protocol::socket> socket; |
186 | |
187 | std::mutex message_lock; |
188 | + SessionCredentials session_creds{0, 0, 0}; |
189 | + |
190 | void send_fds_locked(std::unique_lock<std::mutex> const& lock, std::vector<int32_t> const& fds); |
191 | }; |
192 | } |
193 | |
194 | === modified file 'tests/acceptance-tests/test_trust_session_helper.cpp' |
195 | --- tests/acceptance-tests/test_trust_session_helper.cpp 2014-05-19 02:55:29 +0000 |
196 | +++ tests/acceptance-tests/test_trust_session_helper.cpp 2014-05-20 14:06:48 +0000 |
197 | @@ -27,6 +27,7 @@ |
198 | #include "mir_test_framework/stubbed_server_configuration.h" |
199 | #include "mir_test_framework/basic_client_server_fixture.h" |
200 | #include "mir_test_doubles/fake_ipc_factory.h" |
201 | +#include "mir_test/popen.h" |
202 | |
203 | #include <gtest/gtest.h> |
204 | #include <gmock/gmock.h> |
205 | @@ -149,6 +150,8 @@ |
206 | sprintf(client_connect_string, "fd://%d", fd); |
207 | return client_connect_string; |
208 | } |
209 | + |
210 | + MOCK_METHOD1(process_line, void(std::string const&)); |
211 | }; |
212 | |
213 | void client_fd_callback(MirConnection*, size_t count, int const* fds, void* context) |
214 | @@ -185,3 +188,28 @@ |
215 | |
216 | mir_connection_release(client_connection); |
217 | } |
218 | + |
219 | +// TODO we need a nice way to run this (and similar tests that require a separate client process) in CI |
220 | +// Disabled as we can't be sure the mir_demo_client_basic is about |
221 | +TEST_F(TrustSessionHelper, DISABLED_client_pid_is_associated_with_session) |
222 | +{ |
223 | + auto const server_pid = getpid(); |
224 | + |
225 | + mir_connection_new_fds_for_trusted_clients(connection, 1, &client_fd_callback, this); |
226 | + wait_for_callback(std::chrono::milliseconds(500)); |
227 | + |
228 | + InSequence seq; |
229 | + EXPECT_CALL(*this, process_line(StrEq("Starting"))); |
230 | + EXPECT_CALL(*this, process_line(StrEq("Connected"))); |
231 | + EXPECT_CALL(*this, process_line(StrEq("Surface created"))); |
232 | + EXPECT_CALL(*this, process_line(StrEq("Surface released"))); |
233 | + EXPECT_CALL(*this, process_line(StrEq("Connection released"))); |
234 | + |
235 | + mir::test::Popen output(std::string("bin/mir_demo_client_basic -m ") + fd_connect_string(actual_fds[0])); |
236 | + |
237 | + std::string line; |
238 | + while (output.get_line(line)) process_line(line); |
239 | + |
240 | + EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(0)); |
241 | + EXPECT_THAT(trusted_helper_mediator->client_pid, Ne(server_pid)); |
242 | +} |
PASSED: Continuous integration, rev:1637 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/1647/ jenkins. qa.ubuntu. com/job/ mir-android- utopic- i386-build/ 218 jenkins. qa.ubuntu. com/job/ mir-clang- utopic- amd64-build/ 219 jenkins. qa.ubuntu. com/job/ mir-mediumtests -utopic- touch/219 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- amd64-ci/ 166 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- amd64-ci/ 166/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- armhf-ci/ 165 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- armhf-ci/ 165/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/557 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/557/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/1471 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 7261
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- team-mir- development- branch- ci/1647/ rebuild
http://