summaryrefslogtreecommitdiff
path: root/widget/cocoa/nsSandboxViolationSink.mm
blob: 05721733441e699143aae8def42ead56cc50974c (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
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsSandboxViolationSink.h"

#include <unistd.h>
#include <time.h>
#include <asl.h>
#include <dispatch/dispatch.h>
#include <notify.h>
#include "nsCocoaDebugUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Sprintf.h"

int nsSandboxViolationSink::mNotifyToken = 0;
uint64_t nsSandboxViolationSink::mLastMsgReceived = 0;

void
nsSandboxViolationSink::Start()
{
  if (mNotifyToken) {
    return;
  }
  notify_register_dispatch(SANDBOX_VIOLATION_NOTIFICATION_NAME,
                           &mNotifyToken,
                           dispatch_queue_create(SANDBOX_VIOLATION_QUEUE_NAME,
                                                 DISPATCH_QUEUE_SERIAL),
                           ^(int token) { ViolationHandler(); });
}

void
nsSandboxViolationSink::Stop()
{
  if (!mNotifyToken) {
    return;
  }
  notify_cancel(mNotifyToken);
  mNotifyToken = 0;
}

// We need to query syslogd to find out what violations occurred, and whether
// they were "ours".  We can use the Apple System Log facility to do this.
// Besides calling notify_post("com.apple.sandbox.violation.*"), Apple's
// sandboxd also reports all sandbox violations (sent to it by the Sandbox
// kernel extension) to syslogd, which stores them and makes them viewable
// in the system console.  This is the database we query.

// ViolationHandler() is always called on its own secondary thread.  This
// makes it unlikely it will interfere with other browser activity.

void
nsSandboxViolationSink::ViolationHandler()
{
  aslmsg query = asl_new(ASL_TYPE_QUERY);

  asl_set_query(query, ASL_KEY_FACILITY, "com.apple.sandbox",
                ASL_QUERY_OP_EQUAL);

  // Only get reports that were generated very recently.
  char query_time[30] = {0};
  SprintfLiteral(query_time, "%li", time(NULL) - 2);
  asl_set_query(query, ASL_KEY_TIME, query_time,
                ASL_QUERY_OP_NUMERIC | ASL_QUERY_OP_GREATER_EQUAL);

  // This code is easier to test if we don't just track "our" violations,
  // which are (normally) few and far between.  For example (for the time
  // being at least) four appleeventsd sandbox violations happen every time
  // we start the browser in e10s mode.  But it makes sense to default to
  // only tracking "our" violations.
  if (mozilla::Preferences::GetBool(
      "security.sandbox.mac.track.violations.oursonly", true)) {
    // This makes each of our processes log its own violations.  It might
    // be better to make the chrome process log all the other processes'
    // violations.
    char query_pid[20] = {0};
    SprintfLiteral(query_pid, "%u", getpid());
    asl_set_query(query, ASL_KEY_REF_PID, query_pid, ASL_QUERY_OP_EQUAL);
  }

  aslresponse response = asl_search(nullptr, query);

  // Each time ViolationHandler() is called we grab as many messages as are
  // available.  Otherwise we might not get them all.
  if (response) {
    while (true) {
      aslmsg hit = nullptr;
      aslmsg found = nullptr;
      const char* id_str;

      while ((hit = aslresponse_next(response))) {
        // Record the message id to avoid logging the same violation more
        // than once.
        id_str = asl_get(hit, ASL_KEY_MSG_ID);
        uint64_t id_val = atoll(id_str);
        if (id_val <= mLastMsgReceived) {
          continue;
        }
        mLastMsgReceived = id_val;
        found = hit;
        break;
      }
      if (!found) {
        break;
      }

      const char* pid_str = asl_get(found, ASL_KEY_REF_PID);
      const char* message_str = asl_get(found, ASL_KEY_MSG);
      nsCocoaDebugUtils::DebugLog("nsSandboxViolationSink::ViolationHandler(): id %s, pid %s, message %s",
                                  id_str, pid_str, message_str);
    }
    aslresponse_free(response);
  }
}