summaryrefslogtreecommitdiffstats
path: root/ipc/attachment_broker_privileged_mac.cc
blob: fdf4715b348e00695af90e121426210156851568 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ipc/attachment_broker_privileged_mac.h"

#include <stdint.h>

#include <tuple>

#include "base/mac/mach_port_util.h"
#include "base/mac/scoped_mach_port.h"
#include "base/memory/shared_memory.h"
#include "base/process/port_provider_mac.h"
#include "base/process/process.h"
#include "base/synchronization/lock.h"
#include "ipc/attachment_broker_messages.h"
#include "ipc/brokerable_attachment.h"
#include "ipc/ipc_channel.h"
#include "ipc/mach_port_attachment_mac.h"

namespace IPC {

AttachmentBrokerPrivilegedMac::AttachmentBrokerPrivilegedMac(
    base::PortProvider* port_provider)
    : port_provider_(port_provider) {
  port_provider_->AddObserver(this);
}

AttachmentBrokerPrivilegedMac::~AttachmentBrokerPrivilegedMac() {
  port_provider_->RemoveObserver(this);
  {
    base::AutoLock l(precursors_lock_);
    for (auto it : precursors_)
      delete it.second;
  }
  {
    base::AutoLock l(extractors_lock_);
    for (auto it : extractors_)
      delete it.second;
  }
}

bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess(
    const scoped_refptr<IPC::BrokerableAttachment>& attachment,
    base::ProcessId destination_process) {
  switch (attachment->GetBrokerableType()) {
    case BrokerableAttachment::MACH_PORT: {
      internal::MachPortAttachmentMac* mach_port_attachment =
          static_cast<internal::MachPortAttachmentMac*>(attachment.get());
      MachPortWireFormat wire_format =
          mach_port_attachment->GetWireFormat(destination_process);
      AddPrecursor(wire_format.destination_process,
                   base::mac::ScopedMachSendRight(wire_format.mach_port),
                   wire_format.attachment_id);
      mach_port_attachment->reset_mach_port_ownership();
      SendPrecursorsForProcess(wire_format.destination_process);
      return true;
    }
    default:
      NOTREACHED();
      return false;
  }
  return false;
}

void AttachmentBrokerPrivilegedMac::DeregisterCommunicationChannel(
    Endpoint* endpoint) {
  AttachmentBrokerPrivileged::DeregisterCommunicationChannel(endpoint);

  if (!endpoint)
    return;

  base::ProcessId pid = endpoint->GetPeerPID();
  if (pid == base::kNullProcessId)
    return;

  {
    base::AutoLock l(precursors_lock_);
    auto it = precursors_.find(pid);
    if (it != precursors_.end()) {
      delete it->second;
      precursors_.erase(pid);
    }
  }

  {
    base::AutoLock l(extractors_lock_);
    auto it = extractors_.find(pid);
    if (it != extractors_.end()) {
      delete it->second;
      extractors_.erase(pid);
    }
  }
}

bool AttachmentBrokerPrivilegedMac::OnMessageReceived(const Message& msg) {
  bool handled = true;
  switch (msg.type()) {
    IPC_MESSAGE_HANDLER_GENERIC(AttachmentBrokerMsg_DuplicateMachPort,
                                OnDuplicateMachPort(msg))
    IPC_MESSAGE_UNHANDLED(handled = false)
  }
  return handled;
}

void AttachmentBrokerPrivilegedMac::OnReceivedTaskPort(
    base::ProcessHandle process) {
  SendPrecursorsForProcess(process);
}

AttachmentBrokerPrivilegedMac::AttachmentPrecursor::AttachmentPrecursor(
    const base::ProcessId& pid,
    base::mac::ScopedMachSendRight port,
    const BrokerableAttachment::AttachmentId& id)
    : pid_(pid), port_(port.release()), id_(id) {}

AttachmentBrokerPrivilegedMac::AttachmentPrecursor::~AttachmentPrecursor() {}

base::mac::ScopedMachSendRight
AttachmentBrokerPrivilegedMac::AttachmentPrecursor::TakePort() {
  return base::mac::ScopedMachSendRight(port_.release());
}

AttachmentBrokerPrivilegedMac::AttachmentExtractor::AttachmentExtractor(
    const base::ProcessId& source_pid,
    const base::ProcessId& dest_pid,
    mach_port_name_t port,
    const BrokerableAttachment::AttachmentId& id)
    : source_pid_(source_pid),
      dest_pid_(dest_pid),
      port_to_extract_(port),
      id_(id) {}

AttachmentBrokerPrivilegedMac::AttachmentExtractor::~AttachmentExtractor() {}

void AttachmentBrokerPrivilegedMac::OnDuplicateMachPort(
    const IPC::Message& message) {
  DCHECK_NE(0, message.get_sender_pid());
  AttachmentBrokerMsg_DuplicateMachPort::Param param;
  if (!AttachmentBrokerMsg_DuplicateMachPort::Read(&message, &param)) {
    LogError(ERROR_PARSE_DUPLICATE_MACH_PORT_MESSAGE);
    return;
  }
  IPC::internal::MachPortAttachmentMac::WireFormat wire_format =
      std::get<0>(param);

  if (wire_format.destination_process == base::kNullProcessId) {
    LogError(NO_DESTINATION);
    return;
  }

  AddExtractor(message.get_sender_pid(), wire_format.destination_process,
               wire_format.mach_port, wire_format.attachment_id);
  ProcessExtractorsForProcess(message.get_sender_pid());
}

void AttachmentBrokerPrivilegedMac::RoutePrecursorToSelf(
    AttachmentPrecursor* precursor) {
  DCHECK_EQ(base::Process::Current().Pid(), precursor->pid());

  // Intentionally leak the port, since the attachment takes ownership.
  internal::MachPortAttachmentMac::WireFormat wire_format(
      precursor->TakePort().release(), precursor->pid(), precursor->id());
  scoped_refptr<BrokerableAttachment> attachment(
      new internal::MachPortAttachmentMac(wire_format));
  HandleReceivedAttachment(attachment);
}

bool AttachmentBrokerPrivilegedMac::RouteWireFormatToAnother(
    const MachPortWireFormat& wire_format) {
  DCHECK_NE(wire_format.destination_process, base::Process::Current().Pid());

  // Another process is the destination.
  base::ProcessId dest = wire_format.destination_process;
  base::AutoLock auto_lock(*get_lock());
  AttachmentBrokerPrivileged::EndpointRunnerPair pair =
      GetSenderWithProcessId(dest);
  if (!pair.first) {
    // Assuming that this message was not sent from a malicious process, the
    // channel endpoint that would have received this message will block
    // forever.
    LOG(ERROR) << "Failed to deliver brokerable attachment to process with id: "
               << dest;
    LogError(DESTINATION_NOT_FOUND);
    return false;
  }

  LogError(DESTINATION_FOUND);
  SendMessageToEndpoint(
      pair, new AttachmentBrokerMsg_MachPortHasBeenDuplicated(wire_format));
  return true;
}

base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::ExtractNamedRight(
    mach_port_t task_port,
    mach_port_name_t named_right) {
  mach_port_t extracted_right = MACH_PORT_NULL;
  mach_msg_type_name_t extracted_right_type;
  kern_return_t kr =
      mach_port_extract_right(task_port, named_right, MACH_MSG_TYPE_MOVE_SEND,
                              &extracted_right, &extracted_right_type);
  if (kr != KERN_SUCCESS) {
    LogError(ERROR_EXTRACT_SOURCE_RIGHT);
    return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
  }

  DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
            extracted_right_type);

  return base::mac::ScopedMachSendRight(extracted_right);
}

AttachmentBrokerPrivilegedMac::MachPortWireFormat
AttachmentBrokerPrivilegedMac::CopyWireFormat(
    const MachPortWireFormat& wire_format,
    uint32_t mach_port) {
  return MachPortWireFormat(mach_port, wire_format.destination_process,
                            wire_format.attachment_id);
}

void AttachmentBrokerPrivilegedMac::SendPrecursorsForProcess(
    base::ProcessId pid) {
  base::AutoLock l(precursors_lock_);
  auto it = precursors_.find(pid);
  if (it == precursors_.end())
    return;

  // Whether this process is the destination process.
  bool to_self = pid == base::GetCurrentProcId();

  if (!to_self) {
    base::AutoLock auto_lock(*get_lock());
    AttachmentBrokerPrivileged::EndpointRunnerPair pair =
        GetSenderWithProcessId(pid);
    if (!pair.first) {
      // If there is no sender, then the destination process is no longer
      // running, or never existed to begin with.
      LogError(DESTINATION_NOT_FOUND);
      delete it->second;
      precursors_.erase(it);
      return;
    }
  }

  mach_port_t task_port = port_provider_->TaskForPid(pid);

  // It's possible that the destination process has not yet provided the
  // privileged process with its task port.
  if (!to_self && task_port == MACH_PORT_NULL)
    return;

  while (!it->second->empty()) {
    auto precursor_it = it->second->begin();
    if (to_self) {
      RoutePrecursorToSelf(*precursor_it);
    } else {
      if (!SendPrecursor(*precursor_it, task_port))
        break;
    }
    it->second->erase(precursor_it);
  }

  delete it->second;
  precursors_.erase(it);
}

bool AttachmentBrokerPrivilegedMac::SendPrecursor(
    AttachmentPrecursor* precursor,
    mach_port_t task_port) {
  DCHECK(task_port);
  internal::MachPortAttachmentMac::WireFormat wire_format(
      MACH_PORT_NULL, precursor->pid(), precursor->id());
  base::mac::ScopedMachSendRight port_to_insert = precursor->TakePort();
  mach_port_name_t intermediate_port = MACH_PORT_NULL;
  if (port_to_insert.get() != MACH_PORT_NULL) {
    base::MachCreateError error_code;
    intermediate_port = base::CreateIntermediateMachPort(
        task_port, base::mac::ScopedMachSendRight(port_to_insert.release()),
        &error_code);
    if (intermediate_port == MACH_PORT_NULL) {
      UMAError uma_error;
      switch (error_code) {
        case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT:
          uma_error = ERROR_MAKE_RECEIVE_PORT;
          break;
        case base::MachCreateError::ERROR_SET_ATTRIBUTES:
          uma_error = ERROR_SET_ATTRIBUTES;
          break;
        case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT:
          uma_error = ERROR_EXTRACT_DEST_RIGHT;
          break;
        case base::MachCreateError::ERROR_SEND_MACH_PORT:
          uma_error = ERROR_SEND_MACH_PORT;
          break;
      }
      LogError(uma_error);
    }
  }
  return RouteWireFormatToAnother(
      CopyWireFormat(wire_format, intermediate_port));
}

void AttachmentBrokerPrivilegedMac::AddPrecursor(
    base::ProcessId pid,
    base::mac::ScopedMachSendRight port,
    const BrokerableAttachment::AttachmentId& id) {
  base::AutoLock l(precursors_lock_);
  auto it = precursors_.find(pid);
  if (it == precursors_.end())
    precursors_[pid] = new ScopedVector<AttachmentPrecursor>;

  precursors_[pid]->push_back(new AttachmentPrecursor(
      pid, base::mac::ScopedMachSendRight(port.release()), id));
}

void AttachmentBrokerPrivilegedMac::ProcessExtractorsForProcess(
    base::ProcessId pid) {
  base::AutoLock l(extractors_lock_);
  auto it = extractors_.find(pid);
  if (it == extractors_.end())
    return;

  {
    base::AutoLock auto_lock(*get_lock());
    AttachmentBrokerPrivileged::EndpointRunnerPair pair =
        GetSenderWithProcessId(pid);
    if (!pair.first) {
      // If there is no sender, then the source process is no longer running.
      LogError(ERROR_SOURCE_NOT_FOUND);
      delete it->second;
      extractors_.erase(it);
      return;
    }
  }

  mach_port_t task_port = port_provider_->TaskForPid(pid);

  // It's possible that the source process has not yet provided the privileged
  // process with its task port.
  if (task_port == MACH_PORT_NULL)
    return;

  while (!it->second->empty()) {
    auto extractor_it = it->second->begin();
    ProcessExtractor(*extractor_it, task_port);
    it->second->erase(extractor_it);
  }

  delete it->second;
  extractors_.erase(it);
}

void AttachmentBrokerPrivilegedMac::ProcessExtractor(
    AttachmentExtractor* extractor,
    mach_port_t task_port) {
  DCHECK(task_port);
  base::mac::ScopedMachSendRight send_right =
      ExtractNamedRight(task_port, extractor->port());
  AddPrecursor(extractor->dest_pid(),
               base::mac::ScopedMachSendRight(send_right.release()),
               extractor->id());
  SendPrecursorsForProcess(extractor->dest_pid());
}

void AttachmentBrokerPrivilegedMac::AddExtractor(
    base::ProcessId source_pid,
    base::ProcessId dest_pid,
    mach_port_name_t port,
    const BrokerableAttachment::AttachmentId& id) {
  base::AutoLock l(extractors_lock_);
  DCHECK_NE(base::GetCurrentProcId(), source_pid);

  auto it = extractors_.find(source_pid);
  if (it == extractors_.end())
    extractors_[source_pid] = new ScopedVector<AttachmentExtractor>;

  extractors_[source_pid]->push_back(
      new AttachmentExtractor(source_pid, dest_pid, port, id));
}

}  // namespace IPC